diff --git a/docs/versioned/bookstore/page-2/sentiment-analysis-service-for-bookstore-reviews.md b/docs/versioned/bookstore/page-2/sentiment-analysis-service-for-bookstore-reviews.md index f16f527a662..6ee67b9d5ed 100644 --- a/docs/versioned/bookstore/page-2/sentiment-analysis-service-for-bookstore-reviews.md +++ b/docs/versioned/bookstore/page-2/sentiment-analysis-service-for-bookstore-reviews.md @@ -43,6 +43,7 @@ In order to do so, you need to install the func CLI. You can follow the [officia ???+ success "Verify" Running `func version` in your terminal to verify the installation, and you should see the version of the func CLI you installed. + ???+ bug "Troubleshooting" If you see `command not found`, you may need to add the func CLI to your PATH. @@ -206,9 +207,9 @@ The `sentiment-analysis-app/pyproject.toml` file contains the project configurat Knative Function will automatically install the dependencies listed here when you build the function. ### **Step 4: Build and run your Knative Function locally (Optional)** + ??? info "Click here to expand" - - + ![Image4](images/image4.png) In Knative Function, there are two ways to build: using the [pack build](https://github.com/knative/func/blob/8f3f718a5a036aa6b6eaa9f70c03aeea740015b9/docs/reference/func_build.md?plain=1#L46){:target="_blank"} or using the [source-to-image (s2i) build](https://github.com/knative/func/blob/4f48549c8ad4dad34bf750db243d81d503f0090f/docs/reference/func_build.md?plain=1#L43){:target="_blank"}. @@ -276,7 +277,7 @@ Knative Function will automatically install the dependencies listed here when yo In this case, you will get the full CloudEvent response: - ``` + ```sh Context Attributes, specversion: 1.0 type: new-review-comment @@ -319,7 +320,7 @@ func deploy -b=s2i -v Function deployed in namespace "default" and exposed at URL: http://sentiment-analysis-app.default.svc.cluster.local ``` - + !!! tip You can find the URL of the Knative Function (Knative Service) by running the following command: @@ -334,7 +335,6 @@ func deploy -b=s2i -v sentiment-analysis-app http://sentiment-analysis-app.default.svc.cluster.local sentiment-analysis-app-00001 sentiment-analysis-app-00001 True ``` - ## **Knative Serving: scale down to zero** ![Image13](images/image13.png) @@ -368,7 +368,7 @@ After deployment, the `func` CLI provides a URL to access your function. You can Simply use Knative Function's command `func invoke` to directly send a CloudEvent to the function on your cluster: -```bash +```sh func invoke -f=cloudevent --data='{"reviewText":"I love Knative so much"}' -v ``` @@ -380,7 +380,7 @@ func invoke -f=cloudevent --data='{"reviewText":"I love Knative so much"}' -v If you see the response, it means that the function is running successfully. - ``` + ```sh Context Attributes, specversion: 1.0 type: moderated-comment @@ -399,23 +399,15 @@ func invoke -f=cloudevent --data='{"reviewText":"I love Knative so much"}' -v } ``` +## **Next Step** + ![Image16](images/image16.png) In this tutorial, you learned how to create a serverless function for a simple sentiment analysis service with Knative. -## **Next Step** - -![Image5](images/image5.png) - -Next, we'll deploy another ML service following the same procedure. We encourage you to try it yourself! +Next, we'll deploy another ML service following the same procedure. !!! tip Don't forget to `cd` into the root directory `/start` before proceeding. -If you feel comfortable deploying the other ML service yourself, follow this **simplified guide**: - [Go to Deploy ML workflow: Bad word filter :fontawesome-solid-paper-plane:](../page-3/create-bad-word-filter-service.md){ .md-button .md-button--primary } - -If you encounter any issues, don't worry—we have a detailed tutorial ready for you. - -[Solution - Go to Deploy ML workflow: Bad word filter :fontawesome-solid-paper-plane:](../page-3/solution-create-bad-word-filter-service.md){ .md-button .md-button--primary } diff --git a/docs/versioned/bookstore/page-3/create-bad-word-filter-service.md b/docs/versioned/bookstore/page-3/create-bad-word-filter-service.md index 0258267711e..ba6324ccf37 100644 --- a/docs/versioned/bookstore/page-3/create-bad-word-filter-service.md +++ b/docs/versioned/bookstore/page-3/create-bad-word-filter-service.md @@ -10,11 +10,7 @@ function: tutorial ![Image 4](images/image4.png) -As a bookstore owner, you aim to receive instant notifications in a Slack channel whenever a customer submits a new negative review comment. By leveraging Knative Function, you can set up a serverless function that contains a simple bad word filter service to tell whether the text contains any hateful/insultive speech. - -If you ever get stuck, check the solution here. - -[Solution - Go to Deploy ML workflow: Bad word filter :fontawesome-solid-paper-plane:](../page-3/solution-create-bad-word-filter-service.md){ .md-button .md-button--primary } +As a bookstore owner, you aim to receive instant notifications in a Slack channel whenever a customer submits a new negative review comment. By leveraging Knative Function, you can set up a serverless function that contains a simple bad word filter service to tell whether the text contains any hateful/insulting speech. ## **What Knative features will we learn about?** @@ -59,78 +55,106 @@ func create -l python bad-word-filter The file tree will look like this: - ``` - /start/bad-word-filter - ├── func.yaml + ```txt + start/bad-word-filter ├── .funcignore + ├── function + │   ├── func.py + │   └── __init__.py + ├── func.yaml ├── .gitignore - ├── requirements.txt - ├── app.sh - ├── test_func.py + ├── pyproject.toml ├── README.md - └── Procfile - └── func.py + └── tests + └── test_func.py ``` ### **Step 2: Replace the generated code with the bad word filter logic** ![Image 5](images/image5.png) -`bad-word-filter/func.py` is the file that contains the code for the function. You can replace the generated code with the bad word filter logic. You can use the following code as a starting point: +`bad-word-filter/function/func.py` is the file that contains the code for the function. You can replace the generated code with the bad word filter logic. You can use the following code as a starting point: + +???+ abstract "_bad-word-filter/function/func.py_" -???+ abstract "_bad-word-filter/func.py_" ```python - from parliament import Context - from profanity_check import predict + import logging from cloudevents.http import CloudEvent + from profanity_check import predict + + def new(): + return Function() + + class Function: + async def handle(self, scope, receive, send): + """ Handle all HTTP requests to this Function. The incoming CloudEvent is in scope["event"]. """ + logging.info("Request Received") + + # 1. Extract the CloudEvent from the scope + request_event = scope["event"] - # The function to convert the bad word filter result into a CloudEvent - def create_cloud_event(inputText, data): - attributes = { - "type": "new-review-comment", - "source": "book-review-broker", - "datacontenttype": "application/json", - "badwordfilter": data, - } - - # Put the bad word filter result into a dictionary - data = {"reviewText": inputText, "badWordResult": data} - - # Create a CloudEvent object - event = CloudEvent(attributes, data) - return event - - def inappropriate_language_filter(text): - profanity_result = predict([text["reviewText"]]) - result = "good" - if profanity_result[0] == 1: - result = "bad" - - profanity_event = create_cloud_event(text["reviewText"], result) - return profanity_event - - def main(context: Context): - """ - Function template - The context parameter contains the Flask request object and any - CloudEvent received with the request. - """ - print("Received CloudEvent: ", context.cloud_event) - - # Add your business logic here - return inappropriate_language_filter(context.cloud_event.data) + # 2. Extract the data payload from the event, analyze and create CloudEvent + response_event = self.inappropriate_language_filter(request_event.data) + + # 3. Send the response + logging.info(f"Sending response: {response_event.data}") + await send(response_event) + + def create_cloud_event(self, inputText, data): + attributes = { + "type": "new-review-comment", + "source": "book-review-broker", + "datacontenttype": "application/json", + "badwordfilter": data, + } + + data = {"reviewText": inputText, "badWordResult": data} + + return CloudEvent(attributes, data) + + def inappropriate_language_filter(self, text): + review_text = text.get("reviewText", "") + profanity_result = predict([review_text]) + result = "good" + if profanity_result[0] == 1: + result = "bad" + + return self.create_cloud_event(review_text, result) ``` ### **Step 3: Configure the dependencies** ![Image 8](images/image8.png) -The content of `bad-word-filter/requirements.txt`: - -???+ abstract "_bad-word-filter/requirements.txt_" - ```plaintext - parliament-functions==0.1.0 - alt-profanity-check==1.4.1.post1 - cloudevents==1.10.1 +The content of `bad-word-filter/pyproject.toml`: + +???+ abstract "_bad-word-filter/pyproject.toml_" + + ```toml + [project] + name = "function" + description = "" + version = "0.1.0" + requires-python = ">=3.9" + readme = "README.md" + license = "MIT" + dependencies = [ + "httpx", + "cloudevents", + "pytest", + "pytest-asyncio", + "alt-profanity-check==1.4.1.post1" # <-- add this dependency + ] + authors = [ + { name="Your Name", email="you@example.com"}, + ] + + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + + [tool.pytest.ini_options] + asyncio_mode = "strict" + asyncio_default_fixture_loop_scope = "function" ``` ### **Step 4: Deploy the function to the cluster** @@ -139,12 +163,14 @@ The content of `bad-word-filter/requirements.txt`: !!! note Please enter `/bad-word-filter` when you are executing the following commands. -```plaintext +```sh func deploy -b=s2i -v ``` + ???+ success "Verify" Expect to see the following message: - ``` + + ```sh Function deployed in namespace "default" and exposed at URL: http://bad-word-filter.default.svc.cluster.local ``` @@ -153,14 +179,14 @@ func deploy -b=s2i -v ![Image 7](images/image7.png) -```plaintext +```sh func invoke -f=cloudevent --data='{"reviewText":"I love Knative so much"}' -v ``` ???+ success "Verify" Expect to receive a CloudEvent response: - ```plaintext + ```sh Context Attributes, specversion: 1.0 type: new-review-comment @@ -183,8 +209,8 @@ If you see the response, it means that the function is running successfully. ![Image 9](images/image9.png) -In this tutorial, you learned how to create a serverless function for a simple service that can detect inappropriate languages in text with Knative. +In this tutorial, you learned how to create a serverless function for a simple service that can detect inappropriate languages in text with Knative. -Next, we'll be learning how to use Knative Sequence to connect the 2 ML workflows and make sure they are executed in the order you want. +Next, we'll be learning how to use Knative Sequence to connect the 2 ML workflows and make sure they are executed in the order you want. [Go to Create Knative Sequence :fontawesome-solid-paper-plane:](../page-4/create-sequence-to-streamline-ML-workflows.md){ .md-button .md-button--primary } diff --git a/docs/versioned/bookstore/page-3/solution-create-bad-word-filter-service.md b/docs/versioned/bookstore/page-3/solution-create-bad-word-filter-service.md deleted file mode 100644 index ba743208c2f..00000000000 --- a/docs/versioned/bookstore/page-3/solution-create-bad-word-filter-service.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -audience: developer -components: - - functions - - serving -function: tutorial ---- - -# Solution - Create Bad Word Filter Service - -![image](images/image4.png) - -As a bookstore owner, you aim to receive instant notifications in a Slack channel whenever a customer submits a new negative review comment. By leveraging Knative Function, you can set up a serverless function that contains a simple bad word filter service to tell whether the text contains any hateful/insultive speech. - -## **What Knative features will we learn about?** - -- The easiness to use Knative Function to deploy your service, and make it be managed by Knative Serving, which give you the ability to auto-scale your service to zero, and scale up to handle the demand. - -## **What does the final deliverable look like?** - -![image](images/image2.png) - -A running serverless Knative Function that contains a python application that receives the new review comments as CloudEvent and returns the result that tells your input text contains any inappropriate languages or not. The result is sent back as CloudEvent. - -!!! info - We are using the `profanity_check` library to detect the bad words in the text. It is a open source library. Please see the disclaimer here. The result may not be 100% accurate. - -The function's output will be only from: - -- good -- bad - -## **Implementation** - -![image](images/image10.png) - -The process is straightforward: - -1. Begin by utilizing the `func create` command to generate your code template. -2. Next, incorporate your unique code into this template. -3. Finally, execute `func deploy` to deploy your application seamlessly to the Kubernetes cluster. - -This workflow ensures a smooth transition from development to deployment within the Knative Functions ecosystem. - -???+ bug "Troubleshooting" - If you see `command not found`, you may need to add the func CLI to your PATH.) - -### **Step 1: Create a Knative Function template** - -![image](images/image6.png) - -Create a new function using the `func` CLI: - -```shell -func create -l -``` - -In this case, we are creating a python function, so the command will be: - -```shell -func create -l python bad-word-filter -``` - -This command will create a new directory with the name `bad-word-filter` and a bunch of files in it. The `func` CLI will generate a basic function template for you to start with. - -You can find all the supported language templates [here](https://knative.dev/docs/functions/){:target="_blank"}. - -???+ success "Verify" - The file tree will look like this: - - ``` - /start/bad-word-filter - ├── func.yaml - ├── .funcignore - ├── .gitignore - ├── requirements.txt - ├── app.sh - ├── test_func.py - ├── README.md - ├── Procfile - └── func.py - ``` - -### **Step 2: Replace the generated code with the bad word filter logic** - -![image](images/image5.png) - -`bad-word-filter/func.py` is the file that contains the code for the function. You can replace the generated code with the bad word filter logic. You can use the following code as a starting point: - - - -???+ abstract "_bad-word-filter/func.py_" - ```python - from parliament import Context - from profanity_check import predict - from cloudevents.http import CloudEvent - - # The function to convert the bad word filter result into a CloudEvent - def create_cloud_event(inputText, data): - attributes = { - "type": "new-review-comment", - "source": "book-review-broker", - "datacontenttype": "application/json", - "badwordfilter": data, - } - - # Put the bad word filter result into a dictionary - data = {"reviewText": inputText, "badWordResult": data} - - # Create a CloudEvent object - event = CloudEvent(attributes, data) - return event - - def inappropriate_language_filter(text): - profanity_result = predict([text["reviewText"]]) - result = "good" - if profanity_result[0] == 1: - result = "bad" - - profanity_event = create_cloud_event(text["reviewText"], result) - return profanity_event - - def main(context: Context): - """ - Function template - The context parameter contains the Flask request object and any - CloudEvent received with the request. - """ - print("Received CloudEvent: ", context.cloud_event) - - # Add your business logic here - return inappropriate_language_filter(context.cloud_event.data) - ``` - -### **Step 3: Configure the dependencies** - -![image](images/image8.png) - -The `bad-word-filter/requirements.txt` file contains the dependencies for the function. You can add the following dependencies to the `requirements.txt` file: - -???+ abstract "bad-word-filter/requirements.txt" - ```plaintext - parliament-functions==0.1.0 - alt-profanity-check==1.4.1.post1 - cloudevents==1.10.1 - ``` - -Knative Function will automatically install the dependencies listed here when you build the function. - -### **Step 4: Deploy the function to the cluster** - -![image](images/image1.png) - -!!! note - Please enter `/bad-word-filter` when you are executing the following commands. - -After you have finished the code, you can deploy the function to the cluster using the following command: - -```shell -func deploy -b=s2i -v -``` - -???+ success "Verify" - - When the deployment is complete, you will see the following output: - - ``` - Function deployed in namespace "default" and exposed at URL: - http://bad-word-filter.default.svc.cluster.local - ``` -## **Verify** - -![image](images/image7.png) - -After deployment, the `func` CLI provides a URL to access your function. You can verify the function's operation by sending a request with a sample review comment. - -Simply use Knative Function's command `func invoke` to directly send a CloudEvent to the function on your cluster: - -```shell -func invoke -f=cloudevent --data='{"reviewText":"I love Knative so much"}' -v -``` - -- `-f` flag indicates the type of the data, is either `HTTP` or `cloudevent` -- `--data` flag is the input text -- You can use `-t` flag to specify the URI to the Knative Function. - -???+ success "Verify" - If the function is running successfully, you will see the following output: - - ``` - Context Attributes, - specversion: 1.0 - type: new-review-comment - source: book-review-broker - id: ebbcd761-3a78-4c44-92e3-de575d1f2d38 - time: 2024-05-27T04:44:07.549303Z - datacontenttype: application/json - Extensions, - badwordfilter: good - Data, - { - "reviewText": "I love Knative so much", - "badWordResult": "good" - } - ``` - -## **Next Step** - -![image](images/image9.png) - -In this tutorial, you learned how to create a serverless function for a simple service that can detect the inappropriate languages in text with Knative. - -Next, we'll be learning how to use Knative Sequence to connect the 2 ML workflow and make sure they are executed in the order you want. - -[Go to Create Knative Sequence :fontawesome-solid-paper-plane:](../page-4/create-sequence-to-streamline-ML-workflows.md){ .md-button .md-button--primary } \ No newline at end of file