diff --git a/notebooks/create-oracleaidb26ai-devrel/createadb26ai-dev.md b/notebooks/create-oracleaidb26ai-devrel/createadb26ai-dev.md new file mode 100644 index 00000000..e6c37d9c --- /dev/null +++ b/notebooks/create-oracleaidb26ai-devrel/createadb26ai-dev.md @@ -0,0 +1,300 @@ +# How can I create an Oracle Autonomous AI Database for Developers with Terraform? + +## Introduction + +Autonomous AI Database for Developers provides you with low cost instances for developers and others to build and test new Autonomous AI Database applications. + +Autonomous AI Database for Developers are low-cost, fixed shape databases intended for development and testing uses cases, and are not recommended for production use cases. When you need more compute or storage resources or you want to take advantage of additional Autonomous AI Database features, you can upgrade your Autonomous AI Database for Developers instance to a full paid service instance. + +With Oracle Autonomous AI Database you can work on many tools such as + +* Oracle APEX +* Database Actions +* Graph Studio +* Oracle Machine Learning user interface +* Data Transforms +* Web Access (ORDS) +* MongoDB API +* Data Lake Accelerator +* SODA Drivers + +Estimated Time: 2 to 5 minutes (max). + +### Objectives + +In this short sprint, you will: + +* Create an Oracle Autonomous AI Database for Developers using Oracle Resource Manager Stack. +* View Oracle Autonomous AI Database and Developer Tools. +* (Optional) Create Oracle Autonomous AI Database for Developers using Oracle Terraform Command Line Interface CLI. +* (Optional) Download Database Wallet, Note Connection Details and Create Database User +* Cleanup the resources created. + + [Demo video on Create Oracle Autonomous AI Database](youtube:V2PETW_F7XI:large) + +### Prerequisites + +This lab assumes you have: + +* Oracle cloud account and privileges to create & manage Oracle Autonomous AI Database +* Option 1: [Direct ORM deployment link](https://cloud.oracle.com/resourcemanager/stacks/create?zipUrl=https://objectstorage.us-phoenix-1.oraclecloud.com/p/jtfUsV33KtLR937hWybAgrq8qtuQQuAaIw1K_VBThhlUF6Z1HYF0Ai50sQlp06bQ/n/oradbclouducm/b/medical_transcripts/o/Terraform/oracle-lakehouse-devedition-stack.zip) +* Option 2: Download the source code from the following GitHub location or use this [.zip file direct download link](https://objectstorage.us-phoenix-1.oraclecloud.com/p/jtfUsV33KtLR937hWybAgrq8qtuQQuAaIw1K_VBThhlUF6Z1HYF0Ai50sQlp06bQ/n/oradbclouducm/b/medical_transcripts/o/Terraform/oracle-lakehouse-devedition-stack.zip) + + ``` + -- Clone the GitHub Repo + gh repo clone oracle-devrel/oracle-ai-developer-hub + + or /sourcecode folder + + ``` + +The **source code** folder has two subfolders + +1. The folder **oracle-lakehouse-devedition-cli** has source code for Terraform command line interface, +2. The folder **oracle-lakehouse-devedition-stack** has source code Oracle Resource Manager zip file. you can also directly upload **oracle-lakehouse-devedition-stack.zip** file which has preconfigured values. + + +## Create an Oracle Resource Manager Stack + +1. Create an Autonomous AI Database using the Resource Manager Stack. You can either use the [Direct ORM deployment link](https://cloud.oracle.com/resourcemanager/stacks/create?zipUrl=https://objectstorage.us-phoenix-1.oraclecloud.com/p/jtfUsV33KtLR937hWybAgrq8qtuQQuAaIw1K_VBThhlUF6Z1HYF0Ai50sQlp06bQ/n/oradbclouducm/b/medical_transcripts/o/Terraform/oracle-lakehouse-devedition-stack.zip) or download the source code from the GitHub location mentioned in the Prerequisites section and upload the **oracle-lakehouse-devedition-stack.zip** file. + + > Please Note: Billing for Autonomous AI Database for Developers databases is hourly per instance. please see more info section + +2. Log in to https://cloud.oracle.com from the top left navigation select **Developer Services** and **Stacks** under **Resource Manager** + + ![Navigation](images/01-navigation.png ) + +3. Click on the **Create Stack** button. Upload the .zip file + + ![Create Stack](images/02-createstack.png ) + +4. Provide the Stack Information. You can also accept the default values provided. Ensure that you select the right compartment, click **Next** button and verify the configuration variables, change the password as you prefer. + +5. Ensure that the compartment id is correct, and click **Next** button. + + ![Navigation](images/03-schema.png ) + +6. Review Stack Information. Check on **Run apply** and **Create** button. + + ![Apply](images/04-apply.png ) + + This will create ORM (Oracle Resource Manager) Job. + + ![ORM Job](images/05-orm.png ) + +7. View the logs. + + ![Logs](images/06-logs.png ) + +8. Our AI Database is now created in 1 minute and 15 seconds. + + ![Logs](images/07-logs.png ) + + > Please Note: Sometimes the time to create might slightly change to 2 and a half mins depending on the region and/or internet speed. + + ORM Job Success message + + ![ORM](images/08-orm.png ) + +## View Oracle Autonomous AI Database and Developer Tools + +1. From the left navigation, select **Oracle AI Database** and **Oracle Autonomous AI Database** + + ![ADB view](images/11-adb2.png ) + + +2. View the newly created Oracle Autonomous AI Database, with the Developer tag on the right side of the name. + + ![ADB](images/10-adb.png ) + + Click on the link, which will show all the enabled features and Tools + + ![Tools](images/12-alloptions.png ) + + * Oracle APEX + * Database Actions + * Graph Studio + * Oracle Machine Learning user interface + * Data Transforms + * Web Access (ORDS) + * MongoDB API + * Data Lake Accelerator + * SODA Drivers + +3. Copy the link and open the required tools. + + ![SQL](images/13-sql.png ) + + From the database actions menu, select View all database actions or select **SQL** + + ![SQL Run](images/14-sql2.png ) + + In the **SQL Worksheet**, you can see the default **SH Schema** already pre-created, You can start using this worksheet with an example SQL + + ``` + SELECT * from SH.COUNTRIES + ``` + + Clean up the created resources by going back to the Resource Manager Stack and clicking on the **Destroy** button. + + Click on the Destroy confirmation button. + + +## (Optional) Create Oracle Autonomous AI Database for Developers using Oracle Terraform Command Line Interface CLI + +1. Install Terraform CLI, Please check this link on [Terraform CLI Installation for OCI](https://developer.hashicorp.com/terraform/tutorials/oci-get-started/install-cli) + + ``` + % brew tap hashicorp/tap + % brew install hashicorp/tap/terraform + + -- Verify installation + % terraform -help + + % terraform -v + Terraform v1.13.4 + on darwin_amd64 + ``` + +2. Install OCI CLI, Please refer the [Quick start guide] (https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm) + + ``` + % brew update && brew install oci-cli + + -- Verify installation + % oci -v + 3.62.1 + ``` + +3. Configure OCI CLI, Please check [CLI configuration](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliconfigure.htm) document + + replace , , for example us-phoenix-1, Your SSH Key file with complete path + + ``` + % cat ~/.oci/config + [DEFAULT] + user=ocid1.user.oc1.. + fingerprint= + tenancy=ocid1.tenancy.oc1.. + region= + key_file=/Users//.pem + ``` + +4. View and Edit the Terraform source code main.tf by replacing , , and as appropriate for your tenancy and database creation + + ``` + provider "oci" {} + + resource "oci_database_autonomous_database" "oci_database_autonomous_database" { + autonomous_maintenance_schedule_type = "REGULAR" + compartment_id = "" + compute_count = "4" + compute_model = "ECPU" + data_storage_size_in_gb = "20" + db_name = "" + display_name = "" + admin_password = "" + db_version = "26ai" + db_workload = "LH" + is_dedicated = "false" + is_dev_tier = "true" + is_mtls_connection_required = "true" + is_preview_version_with_service_terms_accepted = "false" + license_model = "LICENSE_INCLUDED" + } + ``` + + Refer [OCI CLI Terraform for Database Creation](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/database_autonomous_database) + +5. Start with Terraform initialisation +6. Run the following commands from the terminal where the **main.tf** file is located. + ``` + terraform init + ``` + + ![Plan](images/17-tfplan.png ) + + ``` + terraform plan -out=myplan.tfplan + ``` + + ![Apply](images/18-tfapply.png ) + + ``` + terraform apply "myplan.tfplan" + ``` + + ![Logs](images/19-tfviewlogs.png ) + + The script has completed resource creation in approximately 3 minutes. Once you have completed using the Oracle Autonomous AI Database 26ai environment. You can destroy the environment + + > Please note: This approach will be slightly slower than running it through the Oracle resource manager stack zip file upload. as you are connecting from your local laptop to OCI using using terraform command line interface. + +## (Optional) Download database wallet, Note connection details and Create database user + +1. From the top right navigation menu click on **Database Connection** button + + ![DB Conn](images/db-conn.png ) + +2. Download wallet + + ![Wallet](images/download-wallet.png ) + + Provide wallet password and save the wallet. + +3. Copy and save TNS Name, which would be of the following format, where database name and region will change. we will need this details for database connection. + + ``` + devdbhs556l_high = (description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1522)(host=adb.us-phoenix-1.oraclecloud.com))(connect_data=(service_name=wkrfs4xeqva1jcu_devdbhs556l_high.adb.oraclecloud.com))(security=(ssl_server_dn_match=yes))) + ``` + +4. Create Database user by clicking on **Database Actions** Menu and **Database Users** + + ![Create User](images/create-user.png ) + + Click on create user button + + ![Create User2](images/create-user2.png ) + + Provide user name and password and select user options + + ![Destroy](images/user-options.png ) + +5. Update **Granted Roles** as required by your application and **Apply Changes** . + + ![Destroy](images/role-grants.png ) + +## Cleanup resources created + +1. Destroy the resources created if you have used using Oracle Stack by clicking on the **Destroy** button. + + ![Destroy](images/15-destroy.png ) + + View Confirmation message logs + + ![Confirm](images/16-destroyconfirm.png ) + +2. If you have used Terraform CLI then run + + ``` + terraform destroy + ``` + + ![Destroy](images/20-destroy.png ) + + -- Enter yes for confirmation + + +## (Optional) Try Live SQL + + + +Live SQL Execution: Sign-In Required
+Duration: 2 minutes + +## Learn More & Downloads + +* [Autonomous AI Database for Developers](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/autonomous-database-for-developers.html) +* [Download Source code](https://github.com/madhusudhanrao-ppm/dbdevrel/tree/main/source-codes) +* [Direct ORM deployment link](https://cloud.oracle.com/resourcemanager/stacks/create?zipUrl=https://objectstorage.us-phoenix-1.oraclecloud.com/p/jtfUsV33KtLR937hWybAgrq8qtuQQuAaIw1K_VBThhlUF6Z1HYF0Ai50sQlp06bQ/n/oradbclouducm/b/medical_transcripts/o/Terraform/oracle-lakehouse-devedition-stack.zip) + \ No newline at end of file diff --git a/notebooks/create-oracleaidb26ai-devrel/images/01-navigation.png b/notebooks/create-oracleaidb26ai-devrel/images/01-navigation.png new file mode 100644 index 00000000..2ee34fd3 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/01-navigation.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/02-createstack.png b/notebooks/create-oracleaidb26ai-devrel/images/02-createstack.png new file mode 100644 index 00000000..f6a87350 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/02-createstack.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/03-schema.png b/notebooks/create-oracleaidb26ai-devrel/images/03-schema.png new file mode 100644 index 00000000..64ae7bef Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/03-schema.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/04-apply.png b/notebooks/create-oracleaidb26ai-devrel/images/04-apply.png new file mode 100644 index 00000000..d1cd9a64 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/04-apply.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/05-orm.png b/notebooks/create-oracleaidb26ai-devrel/images/05-orm.png new file mode 100644 index 00000000..5ac1dc1c Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/05-orm.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/06-logs.png b/notebooks/create-oracleaidb26ai-devrel/images/06-logs.png new file mode 100644 index 00000000..0ce61b6d Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/06-logs.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/07-logs.png b/notebooks/create-oracleaidb26ai-devrel/images/07-logs.png new file mode 100644 index 00000000..5a03d94e Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/07-logs.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/08-orm.png b/notebooks/create-oracleaidb26ai-devrel/images/08-orm.png new file mode 100644 index 00000000..0398e4ff Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/08-orm.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/10-adb.png b/notebooks/create-oracleaidb26ai-devrel/images/10-adb.png new file mode 100644 index 00000000..87f56cd9 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/10-adb.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/11-adb2.png b/notebooks/create-oracleaidb26ai-devrel/images/11-adb2.png new file mode 100644 index 00000000..35b24650 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/11-adb2.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/12-alloptions.png b/notebooks/create-oracleaidb26ai-devrel/images/12-alloptions.png new file mode 100644 index 00000000..024ca08a Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/12-alloptions.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/13-sql.png b/notebooks/create-oracleaidb26ai-devrel/images/13-sql.png new file mode 100644 index 00000000..85fcf6af Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/13-sql.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/14-sql2.png b/notebooks/create-oracleaidb26ai-devrel/images/14-sql2.png new file mode 100644 index 00000000..987e4d5a Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/14-sql2.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/15-destroy.png b/notebooks/create-oracleaidb26ai-devrel/images/15-destroy.png new file mode 100644 index 00000000..0f5ee10b Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/15-destroy.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/16-destroyconfirm.png b/notebooks/create-oracleaidb26ai-devrel/images/16-destroyconfirm.png new file mode 100644 index 00000000..a27cb831 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/16-destroyconfirm.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/17-tfplan.png b/notebooks/create-oracleaidb26ai-devrel/images/17-tfplan.png new file mode 100644 index 00000000..4391fb7a Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/17-tfplan.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/18-tfapply.png b/notebooks/create-oracleaidb26ai-devrel/images/18-tfapply.png new file mode 100644 index 00000000..0e05f3ff Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/18-tfapply.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/19-tfviewlogs.png b/notebooks/create-oracleaidb26ai-devrel/images/19-tfviewlogs.png new file mode 100644 index 00000000..69f3201b Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/19-tfviewlogs.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/20-destroy.png b/notebooks/create-oracleaidb26ai-devrel/images/20-destroy.png new file mode 100644 index 00000000..078ff353 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/20-destroy.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/create-user.png b/notebooks/create-oracleaidb26ai-devrel/images/create-user.png new file mode 100644 index 00000000..a0c7c334 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/create-user.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/create-user2.png b/notebooks/create-oracleaidb26ai-devrel/images/create-user2.png new file mode 100644 index 00000000..eb1798e8 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/create-user2.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/db-conn.png b/notebooks/create-oracleaidb26ai-devrel/images/db-conn.png new file mode 100644 index 00000000..6cd49da2 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/db-conn.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/download-wallet.png b/notebooks/create-oracleaidb26ai-devrel/images/download-wallet.png new file mode 100644 index 00000000..5f425567 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/download-wallet.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/role-grants.png b/notebooks/create-oracleaidb26ai-devrel/images/role-grants.png new file mode 100644 index 00000000..79d14c75 Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/role-grants.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/images/user-options.png b/notebooks/create-oracleaidb26ai-devrel/images/user-options.png new file mode 100644 index 00000000..c7c50f7e Binary files /dev/null and b/notebooks/create-oracleaidb26ai-devrel/images/user-options.png differ diff --git a/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/main.tf b/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/main.tf new file mode 100644 index 00000000..496e6444 --- /dev/null +++ b/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/main.tf @@ -0,0 +1,75 @@ +# Copyright (c) 2025 Oracle and/or its affiliates. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or data +# (collectively the "Software"), free of charge and under any and all copyright +# rights in the Software, and any and all patent rights owned or freely +# licensable by each licensor hereunder covering either (i) the unmodified +# Software as contributed to or provided by such licensor, or (ii) the Larger +# Works (as defined below), to deal in both +# +# (a) the Software, and +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software (each a "Larger Work" to which the Software +# is contributed by such licensors), +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# The above copyright notice and either this complete permission notice or at +# a minimum a reference to the UPL must be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +terraform { + required_version = ">= 0.12.0" +} + +provider "oci" { + tenancy_ocid = var.tenancy_ocid + region = var.region +} + +resource "oci_database_autonomous_database" "autonomous_ai_database" { + compartment_id = var.compartment_ocid + admin_password = var.admin_password + autonomous_maintenance_schedule_type = var.autonomous_maintenance_schedule_type + compute_count = var.compute_count + compute_model = var.compute_model + data_storage_size_in_gb = var.data_storage_size_in_gb + display_name = var.display_name + db_name = local.db_name_trimmed + db_version = var.db_version + db_workload = var.db_workload + is_dedicated = var.is_dedicated + is_dev_tier = var.is_dev_tier + is_mtls_connection_required = var.is_mtls_connection_required + is_preview_version_with_service_terms_accepted = var.is_preview_version_with_service_terms_accepted + license_model = var.license_model +} + +resource "random_string" "db_suffix" { + length = 12 + special = false + upper = false +} +locals { + db_prefix = "DevDB" + db_name = "${local.db_prefix}${random_string.db_suffix.result}" + db_name_trimmed = substr(local.db_name, 0, 11) +} +locals { + timestamp = "${formatdate("YYYY-MM-DD-hhmmss", timestamp())}" +} diff --git a/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/schema.yaml b/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/schema.yaml new file mode 100644 index 00000000..e5501fb1 --- /dev/null +++ b/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/schema.yaml @@ -0,0 +1,39 @@ +# Title shown in Application Information tab. +title: Oracle AI Database 26ai - Developer Edition +# Sub Title shown in Application Information tab. +description: Oracle AI Database 26ai - Developer Edition +stackDescription: "Deploy Oracle AI Database 26ai - Lakehouse Developer Edition" +schemaVersion: 1.1.0 +version: "20251104" +locale: "en" + +logoUrl: "https://cloudmarketplace.oracle.com/marketplace/content?contentId=80556488" + +outputGroups: + - title: Oracle AI Database 26ai - Lakehouse Developer Edition + +variableGroups: + - title: Tenancy Configuration + visible: false + variables: + - tenancy_ocid + - region + + - title: Main Configuration - Required + visible: true + variables: + - compute_count + - compute_model + - data_storage_size_in_gb + - db_name + - display_name + - db_version + - db_workload + - admin_password + - is_dedicated + - is_dev_tier + - is_mtls_connection_required + - is_preview_version_with_service_terms_accepted + - license_model + - autonomous_maintenance_schedule_type + \ No newline at end of file diff --git a/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/variables.tf b/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/variables.tf new file mode 100644 index 00000000..c6b29e76 --- /dev/null +++ b/notebooks/create-oracleaidb26ai-devrel/sourcecode/oracle-lakehouse-devedition-stack/variables.tf @@ -0,0 +1,80 @@ +# Copyright (c) 2025 Oracle and/or its affiliates. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or data +# (collectively the "Software"), free of charge and under any and all copyright +# rights in the Software, and any and all patent rights owned or freely +# licensable by each licensor hereunder covering either (i) the unmodified +# Software as contributed to or provided by such licensor, or (ii) the Larger +# Works (as defined below), to deal in both +# +# (a) the Software, and +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software (each a "Larger Work" to which the Software +# is contributed by such licensors), +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# The above copyright notice and either this complete permission notice or at +# a minimum a reference to the UPL must be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +variable "compartment_ocid" { + description = "The OCID of the compartment where resources will be created." + type = string +} +variable "tenancy_ocid" {} +variable "region" {} +variable "admin_password" { + default = "Welcome123456#" +} +variable "display_name" { + default = "Oracle_AI_Database_26ai_DevRel_DB" +} +variable "compute_count" { + default = "4" +} +variable "autonomous_maintenance_schedule_type" { + default = "REGULAR" +} +variable "compute_model" { + default = "ECPU" +} +variable "data_storage_size_in_gb" { + default = "20" +} +variable "db_version" { + default = "26ai" +} +variable "db_workload" { + default = "LH" +} +variable "is_dedicated" { + default = "false" +} +variable "is_dev_tier" { + default = "true" +} +variable "is_mtls_connection_required" { + default = "true" +} +variable "is_preview_version_with_service_terms_accepted" { + default = "false" +} +variable "license_model" { + default = "LICENSE_INCLUDED" +} diff --git a/notebooks/dbsearch-with-selectai/README-unesco-sites-selectai-colab.md b/notebooks/dbsearch-with-selectai/README-unesco-sites-selectai-colab.md new file mode 100644 index 00000000..ab1beba0 --- /dev/null +++ b/notebooks/dbsearch-with-selectai/README-unesco-sites-selectai-colab.md @@ -0,0 +1,180 @@ +# Oracle SelectAI Colab Notebooks + +This directory contains Google Colab notebooks for demonstrating Oracle AI Database 26ai features, specifically focusing on SelectAI capabilities. + +## Available Notebooks + +### 🆕 UNESCO SITES SelectAI Notebook +**File**: `unesco-sites-selectai-colab.ipynb` + +**Description**: A comprehensive notebook demonstrating Oracle SelectAI with UNESCO World Heritage Sites data, focusing on geographic queries and analysis. + +**Key Features**: +- ✅ Complete wallet-based authentication setup +- ✅ Geographic query examples (region, country, coordinates) +- ✅ Natural language to SQL conversion with Cohere +- ✅ Interactive map visualizations with Folium +- ✅ UNESCO site categorization (Cultural, Natural, Mixed) +- ✅ Advanced geographic analysis queries +- ✅ Comprehensive troubleshooting guide + +**Query Examples**: +- "Show me all UNESCO sites in Europe" +- "Find all cultural heritage sites in Italy" +- "List natural sites with latitude between 40 and 50" +- "Which countries have the most UNESCO sites?" +- "Show me mixed heritage sites in Asia with their coordinates" + +**Prerequisites**: +- Oracle Autonomous Database instance +- Autonomous Database Wallet files are stored in Google Drive +- Alternatively you can also use (Oracle AI Database Docker Container)[https://www.oracle.com/in/database/free/get-started/] +- Cohere API key +- UNESCO_SITES table with World Heritage data (Sample Table)[https://github.com/madhusudhanrao-ppm/code-assets/blob/main/AI-for-Travel/UNESCO_SITES_sample_data.csv] +- UNESCO_SITES table can be created using VSCode SQL Developer Extension or Oracle APEX SQL Commands or SQL Workspace + +```sql + CREATE TABLE "UNESCO_SITES" + ( + "ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE, + "LATITUDE" NUMBER, + "LONGITUDE" NUMBER, + "CATEGORY" VARCHAR2(50), + "STATES_NAME_EN" VARCHAR2(255), + "REGION_EN" VARCHAR2(255), + "NAME_EN" VARCHAR2(255), + PRIMARY KEY ("ID") + USING INDEX ENABLE + ) ; +``` + +## Quick Start + +1. **Upload to Google Colab**: Open the notebook in Google Colab +2. **Configure Connection**: Update connection parameters in Section 3 +3. **Setup Wallet**: Upload your Oracle wallet files to Google Drive +4. **Get API Key**: Obtain a Cohere API key from [Cohere.ai](https://cohere.ai/) +5. **Run Sections**: Execute each section sequentially + +## Technical Requirements + +### Python Libraries +```python +oracledb # Oracle database connectivity +pandas # Data manipulation and analysis +folium # Interactive mapping +geopandas # Geographic data processing +matplotlib # Data visualization +``` + +### Oracle Database Setup +```sql +-- Required privileges +GRANT execute ON DBMS_CLOUD TO DEMOUSER; +GRANT execute ON DBMS_CLOUD_AI TO DEMOUSER; + +-- Network ACL for Cohere API +BEGIN + DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE( + host => 'api.cohere.ai', + ace => xs$ace_type(privilege_list => xs$name_list('http'), + principal_name => 'DEMOUSER', + principal_type => xs_acl.ptype_db) + ); +END; +/ +``` + +## UNESCO SITES Table Structure + +The notebook works with the UNESCO_SITES table containing: +- `ID` - Unique identifier +- `NAME_EN` - Site name in English +- `STATES_NAME_EN` - Country name +- `REGION_EN` - Geographic region +- `CATEGORY` - Site category (Cultural, Natural, Mixed) +- `LATITUDE` - Geographic coordinate +- `LONGITUDE` - Geographic coordinate +- `SHORT_DESCRIPTION_EN` - Site description + +## Geographic Query Categories + +### 1. Region-Based Queries +- Filter by UNESCO regions (Europe, Asia, Africa, etc.) +- Regional site distribution analysis +- Cross-regional comparisons + +### 2. Country-Specific Queries +- Sites within specific countries +- Country-based site counts +- National heritage analysis + +### 3. Coordinate-Based Queries +- Latitude/longitude range filtering +- Proximity analysis +- Geographic coordinate operations + +### 4. Category Analysis +- Cultural vs Natural vs Mixed sites +- Category distribution by region +- Multi-criteria filtering + +## Visualization Features + +### Interactive World Map +- UNESCO site markers with popup information +- Color-coded by site category +- Clickable site details +- Zoom and pan functionality + +### Regional Analysis Charts +- Bar charts showing site distribution +- Regional comparison visualizations +- Category breakdown by region + +## Troubleshooting + +### Common Issues + +1. **Connection Failed** + - Verify wallet path and password + - Check TNS name configuration + - Ensure proper network access + +2. **SelectAI Errors** + - Verify Cohere API key validity + - Check granted privileges + - Ensure profile creation success + +3. **Visualization Issues** + - Install required libraries + - Check coordinate data validity + - Verify Folium compatibility + +### Best Practices + +- Use specific region names (e.g., "Europe and North America") +- Include coordinate ranges for precise queries +- Combine multiple criteria for complex analysis +- Limit result sets for performance with large datasets + +## Contributing + +To add new examples or improve existing notebooks: + +1. Fork the repository +2. Create a feature branch +3. Add your improvements +4. Submit a pull request + +## License + +This project is licensed under the terms specified in the main repository LICENSE file. + +## Support + +For issues or questions: +- Check the troubleshooting section +- Review Oracle SelectAI documentation +- Consult Cohere API documentation +- Open an issue in the repository diff --git a/notebooks/dbsearch-with-selectai/UNESCO_SITES_sample_data.csv b/notebooks/dbsearch-with-selectai/UNESCO_SITES_sample_data.csv new file mode 100644 index 00000000..a73cf261 --- /dev/null +++ b/notebooks/dbsearch-with-selectai/UNESCO_SITES_sample_data.csv @@ -0,0 +1,1114 @@ +LATITUDE,LONGITUDE,CATEGORY,STATES_NAME_EN,REGION_EN,NAME_EN +-42.5,-64,Natural,Argentina,Latin America and the Caribbean,Pennsula Valds +-30,-68,Natural,Argentina,Latin America and the Caribbean,Ischigualasto / Talampaya Natural Parks +-31.42056,-64.19111,Cultural,Argentina,Latin America and the Caribbean,Jesuit Block and Estancias of Córdoba +-23.19986111,-65.34886111,Cultural,Argentina,Latin America and the Caribbean,Quebrada de Humahuaca +-42.8528,-71.8728,Natural,Argentina,Latin America and the Caribbean,Los Alerces National Park +41.095,44.71028,Cultural,Armenia,Europe and North America,Monasteries of Haghpat and Sanahin +-53.1,73.5,Natural,Australia,Asia and the Pacific,Heard and McDonald Islands +-25.48611111,113.4361111,Natural,Australia,Asia and the Pacific,"Shark Bay, Western Australia" +-54.59472222,158.8955556,Natural,Australia,Asia and the Pacific,Macquarie Island +-25.21666667,153.1333333,Natural,Australia,Asia and the Pacific,Fraser Island +-19.08333333,138.7166667,Natural,Australia,Asia and the Pacific,Australian Fossil Mammal Sites (Riversleigh / Naracoorte) +-33.7,150,Natural,Australia,Asia and the Pacific,Greater Blue Mountains Area +40.15889,44.79667,Cultural,Armenia,Europe and North America,Monastery of Geghard and the Upper Azat Valley +40.15931,44.29514,Cultural,Armenia,Europe and North America,Cathedral and Churches of Echmiatsin and the Archaeological Site of Zvartnots +-12.83333333,132.8333333,Mixed,Australia,Asia and the Pacific,Kakadu National Park +-18.28611111,147.7,Natural,Australia,Asia and the Pacific,Great Barrier Reef +-33.85666667,151.2152778,Cultural,Australia,Asia and the Pacific,Sydney Opera House +-34,143,Mixed,Australia,Asia and the Pacific,Willandra Lakes Region +-41.58333333,145.4166667,Mixed,Australia,Asia and the Pacific,Tasmanian Wilderness +-31.56555556,159.0883333,Natural,Australia,Asia and the Pacific,Lord Howe Island Group +-28.25,150.05,Natural,Australia,Asia and the Pacific,Gondwana Rainforests of Australia +-25.33333333,131,Mixed,Australia,Asia and the Pacific,Uluru-Kata Tjuta National Park +-15.65,144.9666667,Natural,Australia,Asia and the Pacific,Wet Tropics of Queensland +34.84694,67.82525,Cultural,Afghanistan,Asia and the Pacific,Cultural Landscape and Archaeological Remains of the Bamiyan Valley +34.39641667,64.51588889,Cultural,Afghanistan,Asia and the Pacific,Minaret and Archaeological Remains of Jam +40.06944444,20.13333333,Cultural,Albania,Europe and North America,Historic Centres of Berat and Gjirokastra +39.75111111,20.02611111,Cultural,Albania,Europe and North America,Butrint +35.81844,4.78684,Cultural,Algeria,Arab States,Al Qal'a of Beni Hammad +25.5,9,Mixed,Algeria,Arab States,Tassili n'Ajjer +32.48333,3.68333,Cultural,Algeria,Arab States,M'Zab Valley +36.32056,5.73667,Cultural,Algeria,Arab States,Djémila +36.55,2.383333333,Cultural,Algeria,Arab States,Tipasa +35.48416667,6.468861111,Cultural,Algeria,Arab States,Timgad +36.78333,3.06028,Cultural,Algeria,Arab States,Kasbah of Algiers +42.49472222,1.595555556,Cultural,Andorra,Europe and North America,Madriu-Perafita-Claror Valley +-6.268888889,14.24972222,Cultural,Angola,Africa,"Mbanza Kongo, Vestiges of the Capital of the former Kingdom of Kongo" +17.00694444,-61.76166667,Cultural,Antigua and Barbuda,Latin America and the Caribbean,Antigua Naval Dockyard and Related Archaeological Sites +-50,-73.24944,Natural,Argentina,Latin America and the Caribbean,Los Glaciares National Park +-25.51805556,-54.13333333,Natural,Argentina,Latin America and the Caribbean,Iguazu National Park +-47.15,-70.66666667,Cultural,Argentina,Latin America and the Caribbean,"Cueva de las Manos, Río Pinturas" +26.23306,50.52722222,Cultural,Bahrain,Arab States,Qal⿿at al-Bahrain ⿿ Ancient Harbour and Capital of Dilmun +26.24128,50.61351,Cultural,Bahrain,Arab States,"Pearling, Testimony of an Island Economy" +26.14972222,50.51277778,Cultural,Bahrain,Arab States,Dilmun Burial Mounds +22.66667,89.8,Cultural,Bangladesh,Asia and the Pacific,Historic Mosque City of Bagerhat +25.03333333,88.98333333,Cultural,Bangladesh,Asia and the Pacific,Ruins of the Buddhist Vihara at Paharpur +21.95,89.18333,Natural,Bangladesh,Asia and the Pacific,The Sundarbans +13.09666667,-59.61388889,Cultural,Barbados,Latin America and the Caribbean,Historic Bridgetown and its Garrison +53.45108333,26.47272222,Cultural,Belarus,Europe and North America,Mir Castle Complex +53.22278,26.69139,Cultural,Belarus,Europe and North America,"Architectural, Residential and Cultural Complex of the Radziwill Family at Nesvizh" +51.03097222,4.47375,Cultural,Belgium,Europe and North America,Flemish Béguinages +50.48111,4.13722,Cultural,Belgium,Europe and North America,"The Four Lifts on the Canal du Centre and their Environs, La Louvière and Le Roeulx (Hainaut)" +50.84668,4.35242,Cultural,Belgium,Europe and North America,"La Grand-Place, Brussels" +51.20891,3.22527,Cultural,Belgium,Europe and North America,Historic Centre of Brugge +50.82806,4.36223,Cultural,Belgium,Europe and North America,Major Town Houses of the Architect Victor Horta (Brussels) +50.43077,3.97879,Cultural,Belgium,Europe and North America,Neolithic Flint Mines at Spiennes (Mons) +50.60603,3.38926,Cultural,Belgium,Europe and North America,Notre-Dame Cathedral in Tournai +51.21833,4.39778,Cultural,Belgium,Europe and North America,Plantin-Moretus House-Workshops-Museum Complex +50.835,4.416111111,Cultural,Belgium,Europe and North America,Stoclet House +50.43527778,3.838333333,Cultural,Belgium,Europe and North America,Major Mining Sites of Wallonia +16.75,-87.05833333,Natural,Belize,Latin America and the Caribbean,Belize Barrier Reef Reserve System +7.183333333,1.983333333,Cultural,Benin,Africa,Royal Palaces of Abomey +-19.58361,-65.75306,Cultural,Bolivia (Plurinational State of),Latin America and the Caribbean,City of Potosí +-16,-60.5,Cultural,Bolivia (Plurinational State of),Latin America and the Caribbean,Jesuit Missions of the Chiquitos +-19.04306,-65.25917,Cultural,Bolivia (Plurinational State of),Latin America and the Caribbean,Historic City of Sucre +-16.55833333,-68.67778,Cultural,Bolivia (Plurinational State of),Latin America and the Caribbean,Tiwanaku: Spiritual and Political Centre of the Tiwanaku Culture +-18.16666667,-63.81666667,Cultural,Bolivia (Plurinational State of),Latin America and the Caribbean,Fuerte de Samaipata +-14.26667,-60.86667,Natural,Bolivia (Plurinational State of),Latin America and the Caribbean,Noel Kempff Mercado National Park +43.34812,17.81092,Cultural,Bosnia and Herzegovina,Europe and North America,Old Bridge Area of the Old City of Mostar +43.78144444,19.288025,Cultural,Bosnia and Herzegovina,Europe and North America,Mehmed Paša SokoloviĿ Bridge in Višegrad +-17.5,128.5,Natural,Australia,Asia and the Pacific,Purnululu National Park +-37.80611111,144.9702778,Cultural,Australia,Asia and the Pacific,Royal Exhibition Building and Carlton Gardens +-33.37833333,150.9944444,Cultural,Australia,Asia and the Pacific,Australian Convict Sites +-22.5625,113.8102778,Natural,Australia,Asia and the Pacific,Ningaloo Coast +-38.08111111,141.8852778,Cultural,Australia,Asia and the Pacific,Budj Bim Cultural Landscape +47.80055556,13.04333333,Cultural,Austria,Europe and North America,Historic Centre of the City of Salzburg +47.64877778,15.82797222,Cultural,Austria,Europe and North America,Semmering Railway +48.18666667,16.31333333,Cultural,Austria,Europe and North America,Palace and Gardens of Schönbrunn +47.55944444,13.64638889,Cultural,Austria,Europe and North America,Hallstatt-Dachstein / Salzkammergut Cultural Landscape +47.07416667,15.39166667,Cultural,Austria,Europe and North America,City of Graz ⿿ Historic Centre and Schloss Eggenberg +48.36444444,15.43416667,Cultural,Austria,Europe and North America,Wachau Cultural Landscape +48.21666667,16.38333333,Cultural,Austria,Europe and North America,Historic Centre of Vienna +40.36666667,49.83333333,Cultural,Azerbaijan,Europe and North America,Walled City of Baku with the Shirvanshah's Palace and Maiden Tower +40.125,49.375,Cultural,Azerbaijan,Europe and North America,Gobustan Rock Art Cultural Landscape +41.20333333,47.1875,Cultural,Azerbaijan,Europe and North America,Historic Centre of Sheki with the Khan⿿s Palace +-18.23333333,-43.6,Cultural,Brazil,Latin America and the Caribbean,Historic Centre of the Town of Diamantina +-16.5,-39.25,Natural,Brazil,Latin America and the Caribbean,Discovery Coast Atlantic Forest Reserves +-24.16667,-48,Natural,Brazil,Latin America and the Caribbean,Atlantic Forest South-East Reserves +-15.93328,-50.13336,Cultural,Brazil,Latin America and the Caribbean,Historic Centre of the Town of Goiás +-2.333333333,-62.00833333,Natural,Brazil,Latin America and the Caribbean,Central Amazon Conservation Complex +-17.71667,-57.38333,Natural,Brazil,Latin America and the Caribbean,Pantanal Conservation Area +-3.857944444,-32.42511111,Natural,Brazil,Latin America and the Caribbean,Brazilian Atlantic Islands: Fernando de Noronha and Atol das Rocas Reserves +-14.00569444,-47.68461111,Natural,Brazil,Latin America and the Caribbean,Cerrado Protected Areas: Chapada dos Veadeiros and Emas National Parks +-22.94777778,-43.29138889,Cultural,Brazil,Latin America and the Caribbean,Rio de Janeiro: Carioca Landscapes between the Mountain and the Sea +42.65611,27.73,Cultural,Bulgaria,Europe and North America,Ancient City of Nessebar +44.11444,27.07806,Natural,Bulgaria,Europe and North America,Srebarna Nature Reserve +41.74272222,23.43047222,Natural,Bulgaria,Europe and North America,Pirin National Park +43.66667,26.66667,Cultural,Bulgaria,Europe and North America,Thracian Tomb of Sveshtari +10.25,-3.583333333,Cultural,Burkina Faso,Africa,Ruins of Loropéni +12.58775833,-3.328986111,Cultural,Burkina Faso,Africa,Ancient ferrous metallurgy sites of Burkina Faso +14.91513889,-23.60519444,Cultural,Cabo Verde,Africa,"Cidade Velha, Historic Centre of Ribeira Grande" +13.43333333,103.8333333,Cultural,Cambodia,Asia and the Pacific,Angkor +14.38833333,104.6838889,Cultural,Cambodia,Asia and the Pacific,Temple of Preah Vihear +48.105,-66.35305556,Natural,Canada,Europe and North America,Miguasha National Park +44.37611111,-64.30916667,Cultural,Canada,Europe and North America,Old Town Lunenburg +44.99438611,-75.765125,Cultural,Canada,Europe and North America,Rideau Canal +45.70972222,-64.43583333,Natural,Canada,Europe and North America,Joggins Fossil Cliffs +45.11833333,-64.30722222,Cultural,Canada,Europe and North America,Landscape of Grand Grand Pré +51.726925,-56.42952222,Cultural,Canada,Europe and North America,Red Bay Basque Whaling Station +12.8725,105.0430556,Cultural,Cambodia,Asia and the Pacific,"Temple Zone of Sambor Prei Kuk, Archaeological Site of Ancient Ishanapura" +3,13,Natural,Cameroon,Africa,Dja Faunal Reserve +51.46666667,-55.61666667,Cultural,Canada,Europe and North America,L⿿Anse aux Meadows National Historic Site +61.54722222,-125.5894444,Natural,Canada,Europe and North America,Nahanni National Park +50.76777778,-111.4922222,Natural,Canada,Europe and North America,Dinosaur Provincial Park +52.095,-131.2202778,Cultural,Canada,Europe and North America,SG_ang Gwaay Llanagaay +49.74944444,-113.6238889,Cultural,Canada,Europe and North America,Head-Smashed-In Buffalo Jump +59.35833333,-112.2933333,Natural,Canada,Europe and North America,Wood Buffalo National Park +46.80944444,-71.21055556,Cultural,Canada,Europe and North America,Historic District of Old Quebec +51.42472222,-116.4797222,Natural,Canada,Europe and North America,Canadian Rocky Mountain Parks +49.6125,-57.53138889,Natural,Canada,Europe and North America,Gros Morne National Park +-11.01611111,-37.21,Cultural,Brazil,Latin America and the Caribbean,São Francisco Square in the Town of São Cristóvão +-23.01860556,-44.68536944,Mixed,Brazil,Latin America and the Caribbean,Paraty and Ilha Grande ⿿ Culture and Biodiversity +-19.85194444,-43.97361111,Cultural,Brazil,Latin America and the Caribbean,Pampulha Modern Ensemble +-22.89711111,-43.18739444,Cultural,Brazil,Latin America and the Caribbean,Valongo Wharf Archaeological Site +42.65,23.26666667,Cultural,Bulgaria,Europe and North America,Boyana Church +43.3,27.15,Cultural,Bulgaria,Europe and North America,Madara Rider +42.61666667,25.4,Cultural,Bulgaria,Europe and North America,Thracian Tomb of Kazanlak +43.71666667,25.96666667,Cultural,Bulgaria,Europe and North America,Rock-Hewn Churches of Ivanovo +42.11666667,23.4,Cultural,Bulgaria,Europe and North America,Rila Monastery +-18.75,21.73333333,Cultural,Botswana,Africa,Tsodilo +-19.28333333,22.9,Natural,Botswana,Africa,Okavango Delta +-20.38888889,-43.50555556,Cultural,Brazil,Latin America and the Caribbean,Historic Town of Ouro Preto +-8.013333333,-34.845,Cultural,Brazil,Latin America and the Caribbean,Historic Centre of the Town of Olinda +-12.96666667,-38.5,Cultural,Brazil,Latin America and the Caribbean,Historic Centre of Salvador de Bahia +-20.49972222,-43.85777778,Cultural,Brazil,Latin America and the Caribbean,Sanctuary of Bom Jesus do Congonhas +-25.68333,-54.43333,Natural,Brazil,Latin America and the Caribbean,Iguaçu National Park +-15.78333,-47.9,Cultural,Brazil,Latin America and the Caribbean,Brasilia +-8.416666667,-42.33333333,Cultural,Brazil,Latin America and the Caribbean,Serra da Capivara National Park +-2.514166667,-44.3025,Cultural,Brazil,Latin America and the Caribbean,Historic Centre of São Luís +35.61167,116.975,Cultural,China,Asia and the Pacific,Temple and Cemetery of Confucius and the Kong Family Mansion in Qufu +32.46667,111,Cultural,China,Asia and the Pacific,Ancient Building Complex in the Wudang Mountains +29.65792,91.11717,Cultural,China,Asia and the Pacific,"Historic Ensemble of the Potala Palace, Lhasa" +29.43333333,115.8666667,Cultural,China,Asia and the Pacific,Lushan National Park +29.5449,103.76925,Mixed,China,Asia and the Pacific,"Mount Emei Scenic Area, including Leshan Giant Buddha Scenic Area" +26.86667,100.23333,Cultural,China,Asia and the Pacific,Old Town of Lijiang +37.20139,112.15444,Cultural,China,Asia and the Pacific,Ancient City of Ping Yao +31.31666667,120.45,Cultural,China,Asia and the Pacific,Classical Gardens of Suzhou +39.91055556,116.1411111,Cultural,China,Asia and the Pacific,"Summer Palace, an Imperial Garden in Beijing" +39.84555556,116.4447222,Cultural,China,Asia and the Pacific,Temple of Heaven: an Imperial Sacrificial Altar in Beijing +27.71667,117.68333,Mixed,China,Asia and the Pacific,Mount Wuyi +29.70111,105.705,Cultural,China,Asia and the Pacific,Dazu Rock Carvings +31.00167,103.60528,Cultural,China,Asia and the Pacific,Mount Qingcheng and the Dujiangyan Irrigation System +29.90444444,117.9875,Cultural,China,Asia and the Pacific,Ancient Villages in Southern Anhui ⿿ Xidi and Hongcun +34.46666667,112.4666667,Cultural,China,Asia and the Pacific,Longmen Grottoes +41.70722222,124.7938889,Cultural,China,Asia and the Pacific,Imperial Tombs of the Ming and Qing Dynasties +40.10972,113.12222,Cultural,China,Asia and the Pacific,Yungang Grottoes +27.895,98.40638889,Natural,China,Asia and the Pacific,Three Parallel Rivers of Yunnan Protected Areas +22.19129194,113.5364611,Cultural,China,Asia and the Pacific,Historic Centre of Macao +23.09327778,102.7799806,Cultural,China,Asia and the Pacific,Cultural Landscape of Honghe Hani Rice Terraces +22.28551944,112.5658611,Cultural,China,Asia and the Pacific,Kaiping Diaolou and Villages +25.02305556,117.6858333,Cultural,China,Asia and the Pacific,Fujian Tulou +36.12666667,114.3138889,Cultural,China,Asia and the Pacific,Yin Xu +51.82641667,-95.41127778,Mixed,Canada,Europe and North America,Pimachiowin Aki +46.635,-53.21111111,Natural,Canada,Europe and North America,Mistaken Point +49.075,-111.6333333,Cultural,Canada,Europe and North America,Writing-on-Stone +9,21.5,Natural,Central African Republic,Africa,Manovo-Gounda St Floris National Park +19.055,20.50555556,Natural,Chad,Africa,Lakes of Ounianga +17.04166667,21.86277778,Mixed,Chad,Africa,Ennedi Massif: Natural and Cultural Landscape +-27.15,-109.45,Cultural,Chile,Latin America and the Caribbean,Rapa Nui National Park +-33.04063889,-71.628,Cultural,Chile,Latin America and the Caribbean,Historic Quarter of the Seaport City of Valparaíso +-42.5,-73.76666667,Cultural,Chile,Latin America and the Caribbean,Churches of Chiloé +-20.20582,-69.79406,Cultural,Chile,Latin America and the Caribbean,Humberstone and Santa Laura Saltpeter Works +-34.08444444,-70.38277778,Cultural,Chile,Latin America and the Caribbean,Sewell Mining Town +36.26667,117.1,Mixed,China,Asia and the Pacific,Mount Taishan +40.41667,116.08333,Cultural,China,Asia and the Pacific,The Great Wall +41.79416667,123.4469444,Cultural,China,Asia and the Pacific,Imperial Palaces of the Ming and Qing Dynasties in Beijing and Shenyang +40.13333,94.81667,Cultural,China,Asia and the Pacific,Mogao Caves +34.38333333,109.1,Cultural,China,Asia and the Pacific,Mausoleum of the First Qin Emperor +39.73333333,115.9166667,Cultural,China,Asia and the Pacific,Peking Man Site at Zhoukoudian +30.16667,118.18333,Mixed,China,Asia and the Pacific,Mount Huangshan +33.08333,103.91667,Natural,China,Asia and the Pacific,Jiuzhaigou Valley Scenic and Historic Interest Area +32.75417,103.82222,Natural,China,Asia and the Pacific,Huanglong Scenic and Historic Interest Area +29.33333,110.5,Natural,China,Asia and the Pacific,Wulingyuan Scenic and Historic Interest Area +40.98694,117.93833,Cultural,China,Asia and the Pacific,"Mountain Resort and its Outlying Temples, Chengde" +34.45874722,113.0677194,Cultural,China,Asia and the Pacific,Historic Monuments of Dengfeng in ⿿The Centre of Heaven and Earth⿝ +30.2375,120.1408333,Cultural,China,Asia and the Pacific,West Lake Cultural Landscape of Hangzhou +28.42194444,106.0425,Natural,China,Asia and the Pacific,China Danxia +24.66888889,102.9772222,Natural,China,Asia and the Pacific,Chengjiang Fossil Site +42.358,116.1851278,Cultural,China,Asia and the Pacific,Site of Xanadu +41.96833333,80.35416667,Natural,China,Asia and the Pacific,Xinjiang Tianshan +34.69388889,112.4683333,Cultural,China,Asia and the Pacific,The Grand Canal +28.99861111,109.9669444,Cultural,China,Asia and the Pacific,Tusi Sites +22.25555556,107.0230556,Cultural,China,Asia and the Pacific,Zuojiang Huashan Rock Art Cultural Landscape +31.46972222,110.2438889,Natural,China,Asia and the Pacific,Hubei Shennongjia +35.38027778,92.43916667,Natural,China,Asia and the Pacific,Qinghai Hoh Xil +24.4475,118.0619444,Cultural,China,Asia and the Pacific,"Kulangsu, a Historic International Settlement" +27.89555556,108.68,Natural,China,Asia and the Pacific,Fanjingshan +30.39555556,119.9908333,Cultural,China,Asia and the Pacific,Archaeological Ruins of Liangzhu City +32.93194444,121.0168139,Natural,China,Asia and the Pacific,Migratory Bird Sanctuaries along the Coast of Yellow Sea-Bohai Gulf of China (Phase I) +5.533333333,-87.06666667,Natural,Costa Rica,Latin America and the Caribbean,Cocos Island National Park +10.85,-85.61666667,Natural,Costa Rica,Latin America and the Caribbean,Area de Conservación Guanacaste +8.911388889,-83.4775,Cultural,Costa Rica,Latin America and the Caribbean,Precolumbian Chiefdom Settlements with Stone Spheres of the Diquís +5.75,-7.66667,Natural,Côte d'Ivoire,Africa,Taï National Park +9,-4,Natural,Côte d'Ivoire,Africa,Comoé National Park +5.195833333,3.736388889,Cultural,Côte d'Ivoire,Africa,Historic Town of Grand-Bassam +42.65056,18.09139,Cultural,Croatia,Europe and North America,Old City of Dubrovnik +43.50944,16.44333,Cultural,Croatia,Europe and North America,Historical Complex of Split with the Palace of Diocletian +44.87778,15.61444,Natural,Croatia,Europe and North America,Plitvice Lakes National Park +20.45,-75,Natural,Cuba,Latin America and the Caribbean,Alejandro de Humboldt National Park +22.61667,-83.71667,Cultural,Cuba,Latin America and the Caribbean,Viñales Valley +19.96666667,-75.87083333,Cultural,Cuba,Latin America and the Caribbean,"San Pedro de la Roca Castle, Santiago de Cuba" +19.88333,-77.63333,Natural,Cuba,Latin America and the Caribbean,Desembarco del Granma National Park +20.03,-75.39138889,Cultural,Cuba,Latin America and the Caribbean,Archaeological Landscape of the First Coffee Plantations in the South-East of Cuba +45.22917,13.59444,Cultural,Croatia,Europe and North America,Episcopal Complex of the Euphrasian Basilica in the Historic Centre of Poreč +43.5125,16.25167,Cultural,Croatia,Europe and North America,Historic City of Trogir +43.73629,15.89038,Cultural,Croatia,Europe and North America,The Cathedral of St James in Šibenik +43.18166667,16.63861111,Cultural,Croatia,Europe and North America,Stari Grad Plain +23.13333333,-82.35,Cultural,Cuba,Latin America and the Caribbean,Old Havana and its Fortification System +21.80305556,-79.98444444,Cultural,Cuba,Latin America and the Caribbean,Trinidad and the Valley de los Ingenios +10.41666667,-75.53333333,Cultural,Colombia,Latin America and the Caribbean,"Port, Fortresses and Group of Monuments, Cartagena" +7.666666667,-77,Natural,Colombia,Latin America and the Caribbean,Los Katíos National Park +9.233333333,-74.43333333,Cultural,Colombia,Latin America and the Caribbean,Historic Centre of Santa Cruz de Mompox +2.583333333,-76.03333333,Cultural,Colombia,Latin America and the Caribbean,National Archeological Park of Tierradentro +1.916666667,-76.23333333,Cultural,Colombia,Latin America and the Caribbean,San Agustín Archaeological Park +5.471666667,-75.68166667,Cultural,Colombia,Latin America and the Caribbean,Coffee Cultural Landscape of Colombia +0.525277778,-72.79722222,Mixed,Colombia,Latin America and the Caribbean,Chiribiquete National Park ⿿ ⿿The Maloca of the Jaguar⿝ +3.966666667,-81.61666667,Natural,Colombia,Latin America and the Caribbean,Malpelo Fauna and Flora Sanctuary +41.15694444,126.1872222,Cultural,China,Asia and the Pacific,Capital Cities and Tombs of the Ancient Koguryo Kingdom +30.83333333,103,Natural,China,Asia and the Pacific,"Sichuan Giant Panda Sanctuaries - Wolong, Mt Siguniang and Jiajin Mountains " +24.92333333,110.3544444,Natural,China,Asia and the Pacific,South China Karst +39.03055556,113.5633333,Cultural,China,Asia and the Pacific,Mount Wutai +28.91583333,118.0644444,Natural,China,Asia and the Pacific,Mount Sanqingshan National Park +-2.5,28.75,Natural,Democratic Republic of the Congo,Africa,Kahuzi-Biega National Park +-2,21,Natural,Democratic Republic of the Congo,Africa,Salonga National Park +2,28.5,Natural,Democratic Republic of the Congo,Africa,Okapi Wildlife Reserve +55.64222222,12.07972222,Cultural,Denmark,Europe and North America,Roskilde Cathedral +56.03889,12.62083333,Cultural,Denmark,Europe and North America,Kronborg Castle +55.75638889,9.42,Cultural,Denmark,Europe and North America,"Jelling Mounds, Runic Stones and Church" +69.13333333,-49.5,Natural,Denmark,Europe and North America,Ilulissat Icefjord +55.26722222,12.42333333,Natural,Denmark,Europe and North America,Stevns Klint +55.35555556,9.481388889,Cultural,Denmark,Europe and North America,"Christiansfeld, a Moravian Church Settlement" +55.91361111,12.35777778,Cultural,Denmark,Europe and North America,The par force hunting landscape in North Zealand +61.16444444,-45.59805556,Cultural,Denmark,Europe and North America,Kujataa Greenland: Norse and Inuit Farming at the Edge of the Ice Cap +67.06393056,-51.43320556,Cultural,Denmark,Europe and North America,Aasivissuit ⿿ Nipisat. Inuit Hunting Ground between Ice and Sea +15.26666667,-61.28333333,Natural,Dominica,Latin America and the Caribbean,Morne Trois Pitons National Park +18.48333333,-69.91666667,Cultural,Dominican Republic,Latin America and the Caribbean,Colonial City of Santo Domingo +-0.81667,-91,Natural,Ecuador,Latin America and the Caribbean,Galápagos Islands +-0.003888889,-78.5,Cultural,Ecuador,Latin America and the Caribbean,City of Quito +-1.83333,-78.33333,Natural,Ecuador,Latin America and the Caribbean,Sangay National Park +-2.883333333,-78.98333333,Cultural,Ecuador,Latin America and the Caribbean,Historic Centre of Santa Ana de los Ríos de Cuenca +29.97604,31.13041,Cultural,Egypt,Arab States,Memphis and its Necropolis ⿿ the Pyramid Fields from Giza to Dahshur +25.73333,32.6,Cultural,Egypt,Arab States,Ancient Thebes with its Necropolis +22.33722222,31.62580556,Cultural,Egypt,Arab States,Nubian Monuments from Abu Simbel to Philae +30.05,31.26111,Cultural,Egypt,Arab States,Historic Cairo +30.83583333,29.66666667,Cultural,Egypt,Arab States,Abu Mena +28.55623,33.97543,Cultural,Egypt,Arab States,Saint Catherine Area +29.33333,30.18333,Natural,Egypt,Arab States,Wadi Al-Hitan (Whale Valley) +13.8275,-89.36916667,Cultural,El Salvador,Latin America and the Caribbean,Joya de Cerén Archaeological Site +15.33527778,38.93583333,Cultural,Eritrea,Africa,Asmara: A Modernist African City +59.43333,24.73333333,Cultural,Estonia,Europe and North America,Historic Centre (Old Town) of Tallinn +13.18333333,38.06666667,Natural,Ethiopia,Africa,Simien National Park +11.10006,40.57939,Cultural,Ethiopia,Africa,Lower Valley of the Awash +8.43491,38.6121,Cultural,Ethiopia,Africa,Tiya +14.13019,38.71861,Cultural,Ethiopia,Africa,Aksum +4.8,35.96666667,Cultural,Ethiopia,Africa,Lower Valley of the Omo +12.02935,39.04042,Cultural,Ethiopia,Africa,"Rock-Hewn Churches, Lalibela" +12.60692,37.46617,Cultural,Ethiopia,Africa,"Fasil Ghebbi, Gondar Region" +9.308888889,42.13777778,Cultural,Ethiopia,Africa,"Harar Jugol, the Fortified Historic Town" +5.3,37.4,Cultural,Ethiopia,Africa,Konso Cultural Landscape +-17.68337778,178.8345333,Cultural,Fiji,Asia and the Pacific,Levuka Historical Port Town +61.12056,21.7775,Cultural,Finland,Europe and North America,Bronze Age Burial Site of Sammallahdenmäki +61.12806,21.51167,Cultural,Finland,Europe and North America,Old Rauma +60.14722,24.98722,Cultural,Finland,Europe and North America,Fortress of Suomenlinna +62.25,25.18333,Cultural,Finland,Europe and North America,Petäjävesi Old Church +61.06194,26.64083,Cultural,Finland,Europe and North America,Verla Groundwood and Board Mill +22.14722,-80.45278,Cultural,Cuba,Latin America and the Caribbean,Urban Historic Centre of Cienfuegos +21.37861111,-77.91861111,Cultural,Cuba,Latin America and the Caribbean,Historic Centre of Camagüey +34.75833,32.40556,Cultural,Cyprus,Europe and North America,Paphos +34.92027778,33.09583333,Cultural,Cyprus,Europe and North America,Painted Churches in the Troodos Region +34.79833,33.34333,Cultural,Cyprus,Europe and North America,Choirokoitia +50.08972,14.41944,Cultural,Czechia,Europe and North America,Historic Centre of Prague +48.81666667,14.31666667,Cultural,Czechia,Europe and North America,Historic Centre of Ŀeský Krumlov +49.18333,15.45,Cultural,Czechia,Europe and North America,Historic Centre of Telč +49.5802,15.94205833,Cultural,Czechia,Europe and North America,Pilgrimage Church of St John of Nepomuk at Zelená Hora +49.95,15.26666667,Cultural,Czechia,Europe and North America,Kutná Hora: Historical Town Centre with the Church of St Barbara and the Cathedral of Our Lady at Sedlec +48.77583,16.775,Cultural,Czechia,Europe and North America,Lednice-Valtice Cultural Landscape +49.59393611,17.25045833,Cultural,Czechia,Europe and North America,Holy Trinity Column in Olomouc +49.3,17.37722222,Cultural,Czechia,Europe and North America,Gardens and Castle at KromĿſíž +48.95972222,14.25277778,Cultural,Czechia,Europe and North America,Holašovice Historic Village +49.87361,16.31444,Cultural,Czechia,Europe and North America,Litomyšl Castle +49.20718333,16.61605556,Cultural,Czechia,Europe and North America,Tugendhat Villa in Brno +49.21722222,15.87888889,Cultural,Czechia,Europe and North America,Jewish Quarter and St Procopius' Basilica in Tſebíč +50.05665,15.48425833,Cultural,Czechia,Europe and North America,Landscape for Breeding and Training of Ceremonial Carriage Horses at Kladruby nad Labem +38.86305556,125.415,Cultural,Democratic People's Republic of Korea,Asia and the Pacific,Complex of Koguryo Tombs +37.98166667,126.5080556,Cultural,Democratic People's Republic of Korea,Asia and the Pacific,Historic Monuments and Sites in Kaesong +0.916666667,29.16666667,Natural,Democratic Republic of the Congo,Africa,Virunga National Park +4,29.25,Natural,Democratic Republic of the Congo,Africa,Garamba National Park +43.95277778,4.806111111,Cultural,France,Europe and North America,"Historic Centre of Avignon: Papal Palace, Episcopal Ensemble and Avignon Bridge" +48.69361111,6.183333333,Cultural,France,Europe and North America,"Place Stanislas, Place de la Carrière and Place d'Alliance in Nancy" +46.56472,0.86611,Cultural,France,Europe and North America,Abbey Church of Saint-Savin sur Gartempe +42.32519444,8.628833333,Natural,France,Europe and North America,"Gulf of Porto: Calanche of Piana, Gulf of Girolata, Scandola Reserve" +43.94722222,4.535277778,Cultural,France,Europe and North America,Pont du Gard (Roman Aqueduct) +43.21055556,2.358888889,Cultural,France,Europe and North America,Historic Fortified City of Carcassonne +48.58444444,7.748888889,Cultural,France,Europe and North America,"Strasbourg, Grande-ÿle and Neustadt" +48.85833333,2.294166667,Cultural,France,Europe and North America,"Paris, Banks of the Seine" +49.25333333,4.032777778,Cultural,France,Europe and North America,"Cathedral of Notre-Dame, Former Abbey of Saint-Rémi and Palace of Tau, Reims" +47.08222222,2.398333333,Cultural,France,Europe and North America,Bourges Cathedral +43.61138889,1.416388889,Cultural,France,Europe and North America,Canal du Midi +45.18405556,0.722944444,Cultural,France,Europe and North America,Routes of Santiago de Compostela in France +45.76722,4.83333,Cultural,France,Europe and North America,Historic Site of Lyon +48.55972222,3.298888889,Cultural,France,Europe and North America,"Provins, Town of Medieval Fairs" +44.89472222,-0.155277778,Cultural,France,Europe and North America,Jurisdiction of Saint-Emilion +47.39889,0.70278,Cultural,France,Europe and North America,The Loire Valley between Sully-sur-Loire and Chalonnes +43.92833333,2.1425,Cultural,France,Europe and North America,Episcopal City of Albi +50.4625,3.546111111,Cultural,France,Europe and North America,Nord-Pas de Calais Mining Basin +47.05805556,4.864444444,Cultural,France,Europe and North America,"The Climats, terroirs of Burgundy" +44.3875,4.416111111,Cultural,France,Europe and North America,"Decorated Cave of Pont d⿿Arc, known as Grotte Chauvet-Pont d⿿Arc, Ardèche" +45.77938889,2.965111111,Natural,France,Europe and North America,Chaîne des Puys - Limagne fault tectonic arena +49.0775,3.946111111,Cultural,France,Europe and North America,"Champagne Hillsides, Houses and Cellars" +49.79278,9.93889,Cultural,Germany,Europe and North America,Würzburg Residence with the Court Gardens and Residence Square +52.15278,9.94389,Cultural,Germany,Europe and North America,St Mary's Cathedral and St Michael's Church at Hildesheim +47.68127778,10.90013889,Cultural,Germany,Europe and North America,Pilgrimage Church of Wies +53.86667,10.69167,Cultural,Germany,Europe and North America,Hanseatic City of Lübeck +50.82502778,6.909777778,Cultural,Germany,Europe and North America,Castles of Augustusburg and Falkenlust at Brühl +50.94111111,6.957222222,Cultural,Germany,Europe and North America,Cologne Cathedral +49.75,6.633333333,Cultural,Germany,Europe and North America,"Roman Monuments, Cathedral of St Peter and Church of Our Lady in Trier" +49.65369,8.56858,Cultural,Germany,Europe and North America,Abbey and Altenmünster of Lorsch +52.4,13.03333333,Cultural,Germany,Europe and North America,Palaces and Parks of Potsdam and Berlin +51.8425,12.42083,Cultural,Germany,Europe and North America,Garden Kingdom of Dessau-Wörlitz +51.78333,11.15,Cultural,Germany,Europe and North America,"Collegiate Church, Castle and Old Town of Quedlinburg" +-16.8414,-151.3723778,Cultural,France,Europe and North America,Taputapuātea +-49.38036111,69.35280556,Natural,France,Europe and North America,French Austral Lands and Seas +-0.5,11.5,Mixed,Gabon,Africa,Ecosystem and Relict Cultural Landscape of Lopé-Okanda +13.31616667,-16.35719444,Cultural,Gambia (the),Africa,Kunta Kinteh Island and Related Sites +41.84389,44.71639,Cultural,Georgia,Europe and North America,Historical Monuments of Mtskheta +42.91639,43.01139,Cultural,Georgia,Europe and North America,Upper Svaneti +42.29472222,42.76833333,Cultural,Georgia,Europe and North America,Gelati Monastery +50.77444444,6.084444444,Cultural,Germany,Europe and North America,Aachen Cathedral +49.31666667,8.443055556,Cultural,Germany,Europe and North America,Speyer Cathedral +-20.41194444,164.5663889,Natural,France,Europe and North America,Lagoons of New Caledonia: Reef Diversity and Associated Ecosystems +44.22027778,3.473055556,Cultural,France,Europe and North America,"The Causses and the Cévennes, Mediterranean agro-pastoral Cultural Landscape" +49.49278,0.1075,Cultural,France,Europe and North America,"Le Havre, the City Rebuilt by Auguste Perret" +44.83888889,-0.572222222,Cultural,France,Europe and North America,"Bordeaux, Port of the Moon" +47.23611111,6.026944444,Cultural,France,Europe and North America,Fortifications of Vauban +-21.09944444,55.48,Natural,France,Europe and North America,"Pitons, cirques and remparts of Reunion Island" +48.63556,-1.51056,Cultural,France,Europe and North America,Mont-Saint-Michel and its Bay +48.4475,1.487222222,Cultural,France,Europe and North America,Chartres Cathedral +48.805,2.119444444,Cultural,France,Europe and North America,Palace and Park of Versailles +47.46638889,3.748333333,Cultural,France,Europe and North America,"Vézelay, Church and Hill" +45.0575,1.17,Cultural,France,Europe and North America,Prehistoric Sites and Decorated Caves of the Vézère Valley +48.40194444,2.698055556,Cultural,France,Europe and North America,Palace and Park of Fontainebleau +49.895,2.301666667,Cultural,France,Europe and North America,Amiens Cathedral +44.13572222,4.808416667,Cultural,France,Europe and North America,"Roman Theatre and its Surroundings and the ""Triumphal Arch"" of Orange" +43.67763889,4.630694444,Cultural,France,Europe and North America,"Arles, Roman and Romanesque Monuments" +47.63944,4.38911,Cultural,France,Europe and North America,Cistercian Abbey of Fontenay +46.9375,5.876388889,Cultural,France,Europe and North America,"From the Great Saltworks of Salins-les-Bains to the Royal Saltworks of Arc-et-Senans, the Production of Open-pan Salt " +49.94444444,11.57861111,Cultural,Germany,Europe and North America,Margravial Opera House Bayreuth +51.31583333,9.393055556,Cultural,Germany,Europe and North America,Bergpark Wilhelmshöhe +51.77827778,9.41025,Cultural,Germany,Europe and North America,Carolingian Westwork and Civitas Corvey +53.54555556,9.999444444,Cultural,Germany,Europe and North America,Speicherstadt and Kontorhaus District with Chilehaus +51.15480556,11.804,Cultural,Germany,Europe and North America,Naumburg Cathedral +48.38777778,9.765555556,Cultural,Germany,Europe and North America,Caves and Ice Age Art in the Swabian Jura +54.46194444,9.454111111,Cultural,Germany,Europe and North America,Archaeological Border complex of Hedeby and the Danevirke +48.36547222,10.902,Cultural,Germany,Europe and North America,Water Management System of Augsburg +5.39103,-0.49361,Cultural,Ghana,Africa,"Forts and Castles, Volta, Greater Accra, Central and Western Regions" +6.401111111,-1.625833333,Cultural,Ghana,Africa,Asante Traditional Buildings +37.43498,21.89694,Cultural,Greece,Europe and North America,Temple of Apollo Epicurius at Bassae +38.48149,22.49617,Cultural,Greece,Europe and North America,Archaeological Site of Delphi +37.97087,23.72618,Cultural,Greece,Europe and North America,"Acropolis, Athens" +40.26667,24.21667,Mixed,Greece,Europe and North America,Mount Athos +39.71667,21.63333,Mixed,Greece,Europe and North America,Meteora +40.63833,22.965,Cultural,Greece,Europe and North America,Paleochristian and Byzantine Monuments of Thessalonika +37.66666667,23.11666667,Cultural,Greece,Europe and North America,Sanctuary of Asklepios at Epidaurus +36.44722,28.22778,Cultural,Greece,Europe and North America,Medieval City of Rhodes +37.08056,22.36667,Cultural,Greece,Europe and North America,Archaeological Site of Mystras +37.65,21.66666667,Cultural,Greece,Europe and North America,Archaeological Site of Olympia +37.4,25.26667,Cultural,Greece,Europe and North America,Delos +38.4,22.75,Cultural,Greece,Europe and North America,"Monasteries of Daphni, Hosios Loukas and Nea Moni of Chios" +37.69083,26.94333,Cultural,Greece,Europe and North America,Pythagoreion and Heraion of Samos +40.47139,22.31833,Cultural,Greece,Europe and North America,Archaeological Site of Aigai (modern name Vergina) +37.73333333,22.75,Cultural,Greece,Europe and North America,Archaeological Sites of Mycenae and Tiryns +37.3,26.55,Cultural,Greece,Europe and North America,The Historic Centre (Chorá) with the Monastery of Saint-John the Theologian and the Cave of the Apocalypse on the Island of Pátmos +39.62394139,19.9275,Cultural,Greece,Europe and North America,Old Town of Corfu +41.01472222,24.28527778,Cultural,Greece,Europe and North America,Archaeological Site of Philippi +17.21666667,-89.61666667,Mixed,Guatemala,Latin America and the Caribbean,Tikal National Park +14.56666667,-90.66666667,Cultural,Guatemala,Latin America and the Caribbean,Antigua Guatemala +15.27059,-89.04025,Cultural,Guatemala,Latin America and the Caribbean,Archaeological Park and Ruins of Quirigua +19.57361,-72.24417,Cultural,Haiti,Latin America and the Caribbean,"National History Park ⿿ Citadel, Sans Souci, Ramiers" +41.90216,12.45736,Cultural,Holy See,Europe and North America,Vatican City +14.85,-89.13333,Cultural,Honduras,Latin America and the Caribbean,Maya Site of Copan +15.74444444,-84.675,Natural,Honduras,Latin America and the Caribbean,Río Plátano Biosphere Reserve +47.48242,19.07067,Cultural,Hungary,Europe and North America,"Budapest, including the Banks of the Danube, the Buda Castle Quarter and Andrássy Avenue" +47.99444,19.52917,Cultural,Hungary,Europe and North America,Old Village of Hollókſ and its Surroundings +47.59458,21.15678,Cultural,Hungary,Europe and North America,Hortobágy National Park - the Puszta +47.55889,17.78444,Cultural,Hungary,Europe and North America,Millenary Benedictine Abbey of Pannonhalma and its Natural Environment +46.07444,18.22778,Cultural,Hungary,Europe and North America,Early Christian Necropolis of Pécs (Sopianae) +48.15,21.35,Cultural,Hungary,Europe and North America,Tokaj Wine Region Historic Cultural Landscape +49.00083,8.81306,Cultural,Germany,Europe and North America,Maulbronn Monastery Complex +51.82,10.34,Cultural,Germany,Europe and North America,"Mines of Rammelsberg, Historic Town of Goslar and Upper Harz Water Management System" +49.89166667,10.88888889,Cultural,Germany,Europe and North America,Town of Bamberg +49.24444,6.85,Cultural,Germany,Europe and North America,Völklingen Ironworks +49.91667,8.75389,Natural,Germany,Europe and North America,Messel Pit Fossil Site +50.97477778,11.3295,Cultural,Germany,Europe and North America,"Bauhaus and its Sites in Weimar, Dessau and Bernau" +51.86472,12.65278,Cultural,Germany,Europe and North America,Luther Memorials in Eisleben and Wittenberg +50.9775,11.32861,Cultural,Germany,Europe and North America,Classical Weimar +52.51972222,13.39861111,Cultural,Germany,Europe and North America,"Museumsinsel (Museum Island), Berlin" +50.96677778,10.307,Cultural,Germany,Europe and North America,Wartburg Castle +47.69872222,9.061305556,Cultural,Germany,Europe and North America,Monastic Island of Reichenau +51.49138889,7.046111111,Cultural,Germany,Europe and North America,Zollverein Coal Mine Industrial Complex in Essen +50.17361111,7.694166667,Cultural,Germany,Europe and North America,Upper Middle Rhine Valley +54.3025,13.08527778,Cultural,Germany,Europe and North America,Historic Centres of Stralsund and Wismar +53.07597222,8.807472222,Cultural,Germany,Europe and North America,Town Hall and Roland on the Marketplace of Bremen +49.02055556,12.09916667,Cultural,Germany,Europe and North America,Old town of Regensburg with Stadtamhof +52.44833333,13.45,Cultural,Germany,Europe and North America,Berlin Modernism Housing Estates +51.98361111,9.811111111,Cultural,Germany,Europe and North America,Fagus Factory in Alfeld +15.94833,75.81667,Cultural,India,Asia and the Pacific,Group of Monuments at Pattadakal +24.85222,79.92222,Cultural,India,Asia and the Pacific,Khajuraho Group of Monuments +15.31444,76.47167,Cultural,India,Asia and the Pacific,Group of Monuments at Hampi +20.55333,75.7,Cultural,India,Asia and the Pacific,Ajanta Caves +20.02639,75.17917,Cultural,India,Asia and the Pacific,Ellora Caves +18.96667,72.93583,Cultural,India,Asia and the Pacific,Elephanta Caves +19.8875,86.09472,Cultural,India,Asia and the Pacific,"Sun Temple, Konârak" +24.88333333,74.64611111,Cultural,India,Asia and the Pacific,Hill Forts of Rajasthan +12.61667,80.19167,Cultural,India,Asia and the Pacific,Group of Monuments at Mahabalipuram +10.78305556,79.1325,Cultural,India,Asia and the Pacific,Great Living Chola Temples +27.18333333,78.03333333,Cultural,India,Asia and the Pacific,Agra Fort +27.17417,78.04222,Cultural,India,Asia and the Pacific,Taj Mahal +27.09444,77.66417,Cultural,India,Asia and the Pacific,Fatehpur Sikri +30.71667,79.66667,Natural,India,Asia and the Pacific,Nanda Devi and Valley of Flowers National Parks +26.66666667,93.41666667,Natural,India,Asia and the Pacific,Kaziranga National Park +26.725,91.03055556,Natural,India,Asia and the Pacific,Manas Wildlife Sanctuary +27.15888889,77.50861111,Natural,India,Asia and the Pacific,Keoladeo National Park +21.945,88.89583333,Natural,India,Asia and the Pacific,Sundarbans National Park +23.47944,77.73972,Cultural,India,Asia and the Pacific,Buddhist Monuments at Sanchi +24.69528,84.99389,Cultural,India,Asia and the Pacific,Mahabodhi Temple Complex at Bodh Gaya +22.48333333,73.53333333,Cultural,India,Asia and the Pacific,Champaner-Pavagadh Archaeological Park +26.92472222,75.825,Cultural,India,Asia and the Pacific,"The Jantar Mantar, Jaipur" +8.529722222,77.24972222,Natural,India,Asia and the Pacific,Western Ghats +31.83333333,77.58333333,Natural,India,Asia and the Pacific,Great Himalayan National Park Conservation Area +-8.54333,119.48944,Natural,Indonesia,Asia and the Pacific,Komodo National Park +-7.75222,110.49167,Cultural,Indonesia,Asia and the Pacific,Prambanan Temple Compounds +-4.75,137.83333,Natural,Indonesia,Asia and the Pacific,Lorentz National Park +-2.5,101.5,Natural,Indonesia,Asia and the Pacific,Tropical Rainforest Heritage of Sumatra +18.92980556,72.83008333,Cultural,India,Asia and the Pacific,Victorian Gothic and Art Deco Ensembles of Mumbai +25.13666667,85.44388889,Cultural,India,Asia and the Pacific,"Archaeological Site of Nalanda Mahavihara at Nalanda, Bihar" +27.76472222,88.37722222,Mixed,India,Asia and the Pacific,Khangchendzonga National Park +23.02638889,72.58805556,Cultural,India,Asia and the Pacific,Historic City of Ahmadabad +26.90786111,75.78722222,Cultural,India,Asia and the Pacific,"Jaipur City, Rajasthan" +-7.60778,110.20361,Cultural,Indonesia,Asia and the Pacific,Borobudur Temple Compounds +-7.4,110.8166667,Cultural,Indonesia,Asia and the Pacific,Sangiran Early Man Site +-6.75,105.3333333,Natural,Indonesia,Asia and the Pacific,Ujung Kulon National Park +23.85888889,72.10166667,Cultural,India,Asia and the Pacific,"Rani-ki-Vav (the Queen⿿s Stepwell) at Patan, Gujarat" +22.92777778,77.58333333,Cultural,India,Asia and the Pacific,Rock Shelters of Bhimbetka +11.51028,76.93583,Cultural,India,Asia and the Pacific,Mountain Railways of India +18.94012222,72.83620278,Cultural,India,Asia and the Pacific,Chhatrapati Shivaji Terminus (formerly Victoria Terminus) +64.25380556,-21.03725,Cultural,Iceland,Europe and North America,ÿingvellir National Park +63.30305556,-20.60222222,Natural,Iceland,Europe and North America,Surtsey +64.577363,-16.88154044,Natural,Iceland,Europe and North America,Vatnajökull National Park - dynamic nature of fire and ice +28.65555556,77.24083333,Cultural,India,Asia and the Pacific,Red Fort Complex +28.59333,77.25056,Cultural,India,Asia and the Pacific,"Humayun's Tomb, Delhi" +28.52583,77.18528,Cultural,India,Asia and the Pacific,"Qutb Minar and its Monuments, Delhi" +15.50222,73.91167,Cultural,India,Asia and the Pacific,Churches and Convents of Goa +32.66972222,51.68527778,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Masjed-e Jāmé of Isfahan +37.25802778,55.169,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Gonbad-e Qābus +35.68036667,51.42051111,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Golestan Palace +30.16805556,55.37555556,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Cultural Landscape of Maymand +32.18944444,48.25611111,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Susa +30.59388889,61.32777778,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Shahr-i Sokhta +30.21611111,58.83888889,Natural,Iran (Islamic Republic of),Asia and the Pacific,Lut Desert +34.29,58.65444444,Cultural,Iran (Islamic Republic of),Asia and the Pacific,The Persian Qanat +31.90138889,54.36916667,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Historic City of Yazd +29.77748056,51.57045,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Sassanid Archaeological Landscape of Fars Region +37.42147222,55.72427778,Natural,Iran (Islamic Republic of),Asia and the Pacific,Hyrcanian Forests +34.34098944,43.82354306,Cultural,Iraq,Arab States,Samarra Archaeological City +35.58806,42.71833,Cultural,Iraq,Arab States,Hatra +32.54196944,44.42083333,Cultural,Iraq,Arab States,Babylon +35.45666667,43.26111111,Cultural,Iraq,Arab States,Ashur (Qal'at Sherqat) +36.19111111,44.00916667,Cultural,Iraq,Arab States,Erbil Citadel +31.56222222,47.65777778,Mixed,Iraq,Arab States,The Ahwar of Southern Iraq: Refuge of Biodiversity and the Relict Landscape of the Mesopotamian Cities +53.69167,-6.45,Cultural,Ireland,Europe and North America,Brú na Bóinne - Archaeological Ensemble of the Bend of the Boyne +51.77194,-10.53861,Cultural,Ireland,Europe and North America,Sceilg Mhichíl +31.3135,35.35275,Cultural,Israel,Europe and North America,Masada +32.92833333,35.08388889,Cultural,Israel,Europe and North America,Old City of Acre +32.06666667,34.78333333,Cultural,Israel,Europe and North America,White City of Tel-Aviv ⿿ the Modern Movement +30.54111,35.16083,Cultural,Israel,Europe and North America,Incense Route - Desert Cities in the Negev +32.59722,35.18222,Cultural,Israel,Europe and North America,"Biblical Tels - Megiddo, Hazor, Beer Sheba" +32.82939667,34.97164889,Cultural,Israel,Europe and North America,Bahá⿿i Holy Places in Haifa and the Western Galilee +31.6,34.89555556,Cultural,Israel,Europe and North America,Caves of Maresha and Bet-Guvrin in the Judean Lowlands as a Microcosm of the Land of the Caves +32.67,34.96527778,Cultural,Israel,Europe and North America,Sites of Human Evolution at Mount Carmel: The Nahal Me⿿arot / Wadi el-Mughara Caves +32.70222222,35.12694444,Cultural,Israel,Europe and North America,Necropolis of Bet She⿿arim: A Landmark of Jewish Renewal +45.46588889,9.1705,Cultural,Italy,Europe and North America,Church and Dominican Convent of Santa Maria delle Grazie with ⿿The Last Supper⿝ by Leonardo da Vinci +45.95705556,10.29733333,Cultural,Italy,Europe and North America,Rock Drawings in Valcamonica +43.77306,11.25611,Cultural,Italy,Europe and North America,Historic Centre of Florence +43.85777778,11.30416667,Cultural,Italy,Europe and North America,Medici Villas and Gardens in Tuscany +45.43430556,12.33894444,Cultural,Italy,Europe and North America,Venice and its Lagoon +43.72305556,10.39638889,Cultural,Italy,Europe and North America,"Piazza del Duomo, Pisa" +41.08480556,16.27094444,Cultural,Italy,Europe and North America,Castel del Monte +41.07333,14.32639,Cultural,Italy,Europe and North America,"18th-Century Royal Palace at Caserta with the Park, the Aqueduct of Vanvitelli, and the San Leucio Complex" +43.46806,11.04167,Cultural,Italy,Europe and North America,Historic Centre of San Gimignano +-8.259166667,115.4027778,Cultural,Indonesia,Asia and the Pacific,Cultural Landscape of Bali Province: the Subak System as a Manifestation of the Tri Hita Karana Philosophy +-0.766625556,100.7378833,Cultural,Indonesia,Asia and the Pacific,Ombilin Coal Mining Heritage of Sawahlunto +32.0833,48.53333333,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Tchogha Zanbil +29.93444,52.89028,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Persepolis +32.65745,51.67777778,Cultural,Iran (Islamic Republic of),Asia and the Pacific,"Meidan Emam, Esfahan" +36.60388889,47.235,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Takht-e Soleyman +30.19383,53.16729,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Pasargadae +36.43528,48.79667,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Soltaniyeh +29.11683,58.36666667,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Bam and its Cultural Landscape +34.38833333,47.43666667,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Bisotun +38.97888889,45.47333333,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Armenian Monastic Ensembles of Iran +32.01861111,48.83583333,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Shushtar Historical Hydraulic System +38.24861111,48.29138889,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Sheikh Safi al-din Khānegāh and Shrine Ensemble in Ardabil +38.08138889,46.29305556,Cultural,Iran (Islamic Republic of),Asia and the Pacific,Tabriz Historic Bazaar Complex +30.16666667,53.16666667,Cultural,Iran (Islamic Republic of),Asia and the Pacific,The Persian Garden +43.07694444,11.67861111,Cultural,Italy,Europe and North America,Historic Centre of the City of Pienza +45.43861111,10.99388889,Cultural,Italy,Europe and North America,City of Verona +45.07253,7.68572,Cultural,Italy,Europe and North America,Residences of the Royal House of Savoy +45.39911111,11.88066667,Cultural,Italy,Europe and North America,"Botanical Garden (Orto Botanico), Padua" +45.76833333,13.3675,Cultural,Italy,Europe and North America,Archaeological Area and the Patriarchal Basilica of Aquileia +44.10694,9.72917,Cultural,Italy,Europe and North America,"Portovenere, Cinque Terre, and the Islands (Palmaria, Tino and Tinetto)" +44.64624,10.92568,Cultural,Italy,Europe and North America,"Cathedral, Torre Civica and Piazza Grande, Modena" +43.725,12.63333,Cultural,Italy,Europe and North America,Historic Centre of Urbino +40.75,14.48333333,Cultural,Italy,Europe and North America,"Archaeological Areas of Pompei, Herculaneum and Torre Annunziata" +40.65,14.6,Cultural,Italy,Europe and North America,Costiera Amalfitana +37.28972222,13.59333333,Cultural,Italy,Europe and North America,Archaeological Area of Agrigento +37.36611,14.33417,Cultural,Italy,Europe and North America,Villa Romana del Casale +36.89319444,15.06891667,Cultural,Italy,Europe and North America,Late Baroque Towns of the Val di Noto (South-Eastern Sicily) +41.96391667,12.79625,Cultural,Italy,Europe and North America,"Villa d'Este, Tivoli" +43.06666667,11.55,Cultural,Italy,Europe and North America,Val d'Orcia +45.97455556,9.169555556,Cultural,Italy,Europe and North America,Sacri Monti of Piedmont and Lombardy +45.15944444,10.79444444,Cultural,Italy,Europe and North America,Mantua and Sabbioneta +46.09416667,13.43305556,Cultural,Italy,Europe and North America,Longobards in Italy. Places of the Power (568-774 A.D.) +44.60861111,7.963611111,Cultural,Italy,Europe and North America,Vineyard Landscape of Piedmont: Langhe-Roero and Monferrato +37.75611111,14.99666667,Natural,Italy,Europe and North America,Mount Etna +38.11083333,13.35305556,Cultural,Italy,Europe and North America,Arab-Norman Palermo and the Cathedral Churches of Cefalú and Monreale +45.4575,7.869166667,Cultural,Italy,Europe and North America,"Ivrea, industrial city of the 20th century" +45.95302778,12.22611111,Cultural,Italy,Europe and North America,Le Colline del Prosecco di Conegliano e Valdobbiadene +42.00683333,12.10188889,Cultural,Italy,Europe and North America,Etruscan Necropolises of Cerveteri and Tarquinia +37.05944,15.29306,Cultural,Italy,Europe and North America,Syracuse and the Rocky Necropolis of Pantalica +44.41222222,8.931111111,Cultural,Italy,Europe and North America,Genoa: Le Strade Nuove and the system of the Palazzi dei Rolli +46.61305556,12.16305556,Natural,Italy,Europe and North America,The Dolomites +39.70583333,8.991388889,Cultural,Italy,Europe and North America,Su Nuraxi di Barumini +40.28333333,15.26666667,Cultural,Italy,Europe and North America,"Cilento and Vallo di Diano National Park with the Archeological Sites of Paestum and Velia, and the Certosa di Padula" +41.94416667,12.77197222,Cultural,Italy,Europe and North America,Villa Adriana (Tivoli) +38.48786111,14.94558333,Natural,Italy,Europe and North America,Isole Eolie (Aeolian Islands) +43.06616667,12.62244444,Cultural,Italy,Europe and North America,"Assisi, the Basilica of San Francesco and Other Franciscan Sites" +40.66638889,16.61027778,Cultural,Italy,Europe and North America,The Sassi and the Park of the Rupestrian Churches of Matera +45.54916667,11.54944444,Cultural,Italy,Europe and North America,City of Vicenza and the Palladian Villas of the Veneto +43.31861111,11.33166667,Cultural,Italy,Europe and North America,Historic Centre of Siena +40.85138889,14.26277778,Cultural,Italy,Europe and North America,Historic Centre of Naples +45.59333,9.53833,Cultural,Italy,Europe and North America,Crespi d'Adda +44.83777778,11.61944444,Cultural,Italy,Europe and North America,"Ferrara, City of the Renaissance, and its Po Delta" +40.7825,17.23694,Cultural,Italy,Europe and North America,The Trulli of Alberobello +44.42041667,12.19625,Cultural,Italy,Europe and North America,Early Christian Monuments of Ravenna +34.43055556,131.4122222,Cultural,Japan,Asia and the Pacific,"Sites of Japan⿿s Meiji Industrial Revolution: Iron and Steel, Shipbuilding and Coal Mining" +32.80222222,128.9038889,Cultural,Japan,Asia and the Pacific,Hidden Christian Sites in the Nagasaki Region +34.245,130.1055556,Cultural,Japan,Asia and the Pacific,Sacred Island of Okinoshima and Associated Sites in the Munakata Region +34.56222222,135.6094444,Cultural,Japan,Asia and the Pacific,Mozu-Furuichi Kofun Group: Mounded Tombs of Ancient Japan +31.78333333,35.21666667,Cultural,Jerusalem (Site proposed by Jordan),Arab States,Old City of Jerusalem and its Walls +30.33056,35.44333,Cultural,Jordan,Arab States,Petra +31.80194,36.58583,Cultural,Jordan,Arab States,Quseir Amra +31.50167,35.92056,Cultural,Jordan,Arab States,Um er-Rasas (Kastrom Mefa'a) +29.63972222,35.43388889,Mixed,Jordan,Arab States,Wadi Rum Protected Area +31.83722222,35.55277778,Cultural,Jordan,Arab States,Baptism Site ⿿Bethany Beyond the Jordan⿝ (Al-Maghtas) +50.43333333,69.18888889,Natural,Kazakhstan,Asia and the Pacific,Saryarka ⿿ Steppe and Lakes of Northern Kazakhstan +43.29305556,68.27444444,Cultural,Kazakhstan,Asia and the Pacific,Mausoleum of Khoja Ahmed Yasawi +43.80333333,75.535,Cultural,Kazakhstan,Asia and the Pacific,Petroglyphs within the Archaeological Landscape of Tamgaly +-0.155,37.31555556,Natural,Kenya,Africa,Mount Kenya National Park/Natural Forest +3.051305556,36.50366667,Natural,Kenya,Africa,Lake Turkana National Parks +-2.284444444,40.8525,Cultural,Kenya,Africa,Lamu Old Town +-0.4425,36.24,Natural,Kenya,Africa,Kenya Lake System in the Great Rift Valley +-3.931944444,39.59611111,Cultural,Kenya,Africa,Sacred Mijikenda Kaya Forests +-4.062777778,39.67944444,Cultural,Kenya,Africa,"Fort Jesus, Mombasa" +-0.891338056,34.326107,Cultural,Kenya,Africa,Thimlich Ohinga Archaeological Site +-3.649722222,-172.8575,Natural,Kiribati,Asia and the Pacific,Phoenix Islands Protected Area +40.53111111,72.78277778,Cultural,Kyrgyzstan,Asia and the Pacific,Sulaiman-Too Sacred Mountain +19.88889,102.13333,Cultural,Lao People's Democratic Republic,Asia and the Pacific,Town of Luang Prabang +14.84833333,105.8222222,Cultural,Lao People's Democratic Republic,Asia and the Pacific,Vat Phou and Associated Ancient Settlements within the Champasak Cultural Landscape +19.43105556,103.1522222,Cultural,Lao People's Democratic Republic,Asia and the Pacific,Megalithic Jar Sites in Xiengkhuang ⿿ Plain of Jars +56.95417,24.11667,Cultural,Latvia,Europe and North America,Historic Centre of Riga +33.72583,35.92972,Cultural,Lebanon,Arab States,Anjar +34.00707,36.20494,Cultural,Lebanon,Arab States,Baalbek +34.11917,35.6475,Cultural,Lebanon,Arab States,Byblos +33.27194,35.19444,Cultural,Lebanon,Arab States,Tyre +34.24333,36.04889,Cultural,Lebanon,Arab States,Ouadi Qadisha (the Holy Valley) and the Forest of the Cedars of God (Horsh Arz el-Rab) +32.63833,14.29306,Cultural,Libya,Arab States,Archaeological Site of Leptis Magna +32.80528,12.485,Cultural,Libya,Arab States,Archaeological Site of Sabratha +32.825,21.85833,Cultural,Libya,Arab States,Archaeological Site of Cyrene +24.83333,10.33333,Cultural,Libya,Arab States,Rock-Art Sites of Tadrart Acacus +30.13333333,9.5,Cultural,Libya,Arab States,Old Town of Ghadamès +54.68667,25.29306,Cultural,Lithuania,Europe and North America,Vilnius Historic Centre +54.88777778,24.83055556,Cultural,Lithuania,Europe and North America,KernavĿ Archaeological Site (Cultural Reserve of KernavĿ) +49.61,6.13333,Cultural,Luxembourg,Europe and North America,City of Luxembourg: its Old Quarters and Fortifications +-18.66667,44.75,Natural,Madagascar,Africa,Tsingy de Bemaraha Strict Nature Reserve +18.0775,-76.57111111,Mixed,Jamaica,Latin America and the Caribbean,Blue and John Crow Mountains +34.61666667,135.7333333,Cultural,Japan,Asia and the Pacific,Buddhist Monuments in the Horyu-ji Area +34.83333333,134.7,Cultural,Japan,Asia and the Pacific,Himeji-jo +30.33333333,130.5333333,Natural,Japan,Asia and the Pacific,Yakushima +40.47,140.13,Natural,Japan,Asia and the Pacific,Shirakami-Sanchi +34.98055556,135.7694444,Cultural,Japan,Asia and the Pacific,"Historic Monuments of Ancient Kyoto (Kyoto, Uji and Otsu Cities)" +36.4,136.8833333,Cultural,Japan,Asia and the Pacific,Historic Villages of Shirakawa-go and Gokayama +34.38333333,132.45,Cultural,Japan,Asia and the Pacific,Hiroshima Peace Memorial (Genbaku Dome) +34.29441667,132.3246389,Cultural,Japan,Asia and the Pacific,Itsukushima Shinto Shrine +34.67555556,135.8394444,Cultural,Japan,Asia and the Pacific,Historic Monuments of Ancient Nara +36.7475,139.6105556,Cultural,Japan,Asia and the Pacific,Shrines and Temples of Nikko +26.20861111,127.6827778,Cultural,Japan,Asia and the Pacific,Gusuku Sites and Related Properties of the Kingdom of Ryukyu +33.83694444,135.7763889,Cultural,Japan,Asia and the Pacific,Sacred Sites and Pilgrimage Routes in the Kii Mountain Range +43.94944,144.96583,Natural,Japan,Asia and the Pacific,Shiretoko +35.11277778,132.435,Cultural,Japan,Asia and the Pacific,Iwami Ginzan Silver Mine and its Cultural Landscape +39.00111111,141.1077778,Cultural,Japan,Asia and the Pacific,"Hiraizumi ⿿ Temples, Gardens and Archaeological Sites Representing the Buddhist Pure Land" +27.71833333,142.0997222,Natural,Japan,Asia and the Pacific,Ogasawara Islands +35.36083333,138.7275,Cultural,Japan,Asia and the Pacific,"Fujisan, sacred place and source of artistic inspiration" +36.25527778,138.8877778,Cultural,Japan,Asia and the Pacific,Tomioka Silk Mill and Related Sites +5.421388889,100.3458333,Cultural,Malaysia,Asia and the Pacific,"Melaka and George Town, Historic Cities of the Straits of Malacca" +5.067908333,100.9723278,Cultural,Malaysia,Asia and the Pacific,Archaeological Heritage of the Lenggong Valley +13.90639,-4.555,Cultural,Mali,Africa,Old Towns of Djenné +16.77333333,-2.999444444,Cultural,Mali,Africa,Timbuktu +14.33333,-3.41667,Mixed,Mali,Africa,Cliff of Bandiagara (Land of the Dogons) +16.2898,-0.04456,Cultural,Mali,Africa,Tomb of Askia +35.87134,14.50739,Cultural,Malta,Europe and North America,Ħal Saflieni Hypogeum +35.90056,14.51444,Cultural,Malta,Europe and North America,City of Valletta +36.04908,14.26947,Cultural,Malta,Europe and North America,Megalithic Temples of Malta +11.6,165.3805556,Cultural,Marshall Islands,Asia and the Pacific,Bikini Atoll Nuclear Test Site +20.23472,-16.10889,Natural,Mauritania,Arab States,Banc d'Arguin National Park +20.92889,-11.62361,Cultural,Mauritania,Arab States,"Ancient Ksour of Ouadane, Chinguetti, Tichitt and Oualata" +-20.15863889,57.50316667,Cultural,Mauritius,Africa,Aapravasi Ghat +-20.45194444,57.32833333,Cultural,Mauritius,Africa,Le Morne Cultural Landscape +19.38333,-87.79167,Natural,Mexico,Latin America and the Caribbean,Sian Ka'an +17.48333,-92.05,Cultural,Mexico,Latin America and the Caribbean,Pre-Hispanic City and National Park of Palenque +19.41833,-99.13278,Cultural,Mexico,Latin America and the Caribbean,Historic Centre of Mexico City and Xochimilco +19.69167,-98.84167,Cultural,Mexico,Latin America and the Caribbean,Pre-Hispanic City of Teotihuacan +18.93472,-98.89778,Cultural,Mexico,Latin America and the Caribbean,Earliest 16th-Century Monasteries on the Slopes of Popocatepetl +27.65556,-112.91611,Cultural,Mexico,Latin America and the Caribbean,Rock Paintings of the Sierra de San Francisco +20.36166667,-89.77027778,Cultural,Mexico,Latin America and the Caribbean,Pre-Hispanic Town of Uxmal +20.58333333,-100.3666667,Cultural,Mexico,Latin America and the Caribbean,Historic Monuments Zone of Querétaro +20.67388889,-103.3397222,Cultural,Mexico,Latin America and the Caribbean,"Hospicio Cabañas, Guadalajara" +18.60833333,-95.65833333,Cultural,Mexico,Latin America and the Caribbean,Historic Monuments Zone of Tlacotalpan +19.84639,-90.53722,Cultural,Mexico,Latin America and the Caribbean,Historic Fortified Town of Campeche +27.62667,-112.54583,Natural,Mexico,Latin America and the Caribbean,Islands and Protected Areas of the Gulf of California +20.86305556,-103.7786111,Cultural,Mexico,Latin America and the Caribbean,Agave Landscape and Ancient Industrial Facilities of Tequila +19.33222222,-99.18805556,Cultural,Mexico,Latin America and the Caribbean,Central University City Campus of the Universidad Nacional Autónoma de México (UNAM) +20.91444444,-100.7463889,Cultural,Mexico,Latin America and the Caribbean,Protective town of San Miguel and the Sanctuary of Jesús Nazareno de Atotonilco +18.81028,-99.275,Cultural,Mexico,Latin America and the Caribbean,Archaeological Monuments Zone of Xochicalco +18.05302778,-89.73728333,Mixed,Mexico,Latin America and the Caribbean,"Ancient Maya City and Protected Tropical Forests of Calakmul, Campeche" +21.20438889,-99.46411111,Cultural,Mexico,Latin America and the Caribbean,Franciscan Missions in the Sierra Gorda of Querétaro +19.41833333,-99.19833333,Cultural,Mexico,Latin America and the Caribbean,Luis Barragán House and Studio +17.06194,-96.72167,Cultural,Mexico,Latin America and the Caribbean,Historic Centre of Oaxaca and Archaeological Site of Monte Albán +19.04722,-98.20833,Cultural,Mexico,Latin America and the Caribbean,Historic Centre of Puebla +21.01694,-101.25556,Cultural,Mexico,Latin America and the Caribbean,Historic Town of Guanajuato and Adjacent Mines +20.66667,-88.6,Cultural,Mexico,Latin America and the Caribbean,Pre-Hispanic City of Chichen-Itza +27.79222,-114.22778,Natural,Mexico,Latin America and the Caribbean,Whale Sanctuary of El Vizcaino +30.37583,-107.95556,Cultural,Mexico,Latin America and the Caribbean,"Archaeological Zone of Paquimé, Casas Grandes" +19.70444,-101.19167,Cultural,Mexico,Latin America and the Caribbean,Historic Centre of Morelia +20.47639,-97.3775,Cultural,Mexico,Latin America and the Caribbean,"El Tajin, Pre-Hispanic City" +22.76667,-102.55556,Cultural,Mexico,Latin America and the Caribbean,Historic Centre of Zacatecas +-18.75917,47.56278,Cultural,Madagascar,Africa,Royal Hill of Ambohimanga +-14.45972222,49.7025,Natural,Madagascar,Africa,Rainforests of the Atsinanana +-14.03333,34.88333,Natural,Malawi,Africa,Lake Malawi National Park +-14.29333333,34.27916667,Cultural,Malawi,Africa,Chongoni Rock-Art Area +6.25,116.5,Natural,Malaysia,Asia and the Pacific,Kinabalu Park +4.13333,114.91667,Natural,Malaysia,Asia and the Pacific,Gunung Mulu National Park +34.02416667,-6.822777778,Cultural,Morocco,Arab States,"Rabat, Modern Capital and Historic City: a Shared Heritage" +-15.03417,40.73583,Cultural,Mozambique,Africa,Island of Mozambique +22.47,95.81861111,Cultural,Myanmar,Asia and the Pacific,Pyu Ancient Cities +21.14888889,94.88444444,Cultural,Myanmar,Asia and the Pacific,Bagan +-20.59558333,14.37258333,Cultural,Namibia,Africa,Twyfelfontein or /Ui-//aes +-24.88527778,15.40777778,Natural,Namibia,Africa,Namib Sand Sea +27.96528,86.91306,Natural,Nepal,Asia and the Pacific,Sagarmatha National Park +27.70395,85.30858,Cultural,Nepal,Asia and the Pacific,Kathmandu Valley +27.5,84.33333,Natural,Nepal,Asia and the Pacific,Chitwan National Park +27.46889,83.27611,Cultural,Nepal,Asia and the Pacific,"Lumbini, the Birthplace of the Lord Buddha" +52.63861111,5.771666667,Cultural,Netherlands,Europe and North America,Schokland and Surroundings +52.37444444,4.893055556,Cultural,Netherlands,Europe and North America,Defence Line of Amsterdam +51.8825,4.649444444,Cultural,Netherlands,Europe and North America,Mill Network at Kinderdijk-Elshout +12.10194,-68.90222,Cultural,Netherlands,Europe and North America,"Historic Area of Willemstad, Inner City and Harbour, Curaçao" +52.84583,5.67889,Cultural,Netherlands,Europe and North America,Ir.D.F. Woudagemaal (D.F. Wouda Steam Pumping Station) +52.54888889,4.911111111,Cultural,Netherlands,Europe and North America,Droogmakerij de Beemster (Beemster Polder) +52.08533333,5.147555556,Cultural,Netherlands,Europe and North America,Rietveld Schröderhuis (Rietveld Schröder House) +52.365,4.887777778,Cultural,Netherlands,Europe and North America,Seventeenth-Century Canal Ring Area of Amsterdam inside the Singelgracht +51.92333333,4.418333333,Cultural,Netherlands,Europe and North America,Van Nellefabriek +-39.29083333,175.5622222,Mixed,New Zealand,Asia and the Pacific,Tongariro National Park +-45.03602778,167.3196111,Natural,New Zealand,Asia and the Pacific,Te Wahipounamu ⿿ South West New Zealand +-50.75,166.1044444,Natural,New Zealand,Asia and the Pacific,New Zealand Sub-Antarctic Islands +12.39722,-86.61028,Cultural,Nicaragua,Latin America and the Caribbean,Ruins of León Viejo +12.435,-86.87805556,Cultural,Nicaragua,Latin America and the Caribbean,León Cathedral +18,9,Natural,Niger,Africa,Air and Ténéré Natural Reserves +16.97361111,7.991388889,Cultural,Niger,Africa,Historic Centre of Agadez +10.74056,13.57194,Cultural,Nigeria,Africa,Sukur Cultural Landscape +7.75556,4.55222,Cultural,Nigeria,Africa,Osun-Osogbo Sacred Grove +62.57388889,11.38555556,Cultural,Norway,Europe and North America,Røros Mining Town and the Circumference +61.3,7.33333,Cultural,Norway,Europe and North America,Urnes Stave Church +60.39722,5.32306,Cultural,Norway,Europe and North America,Bryggen +69.95,23.18333,Cultural,Norway,Europe and North America,Rock Art of Alta +65.61667,11.75,Cultural,Norway,Europe and North America,Vegaøyan ⿿ The Vega Archipelago +62.11667,7.16667,Natural,Norway,Europe and North America,West Norwegian Fjords ⿿ Geirangerfjord and Nærøyfjord +59.87861111,8.593611111,Cultural,Norway,Europe and North America,Rjukan-Notodden Industrial Heritage Site +22.96417,57.30111,Cultural,Oman,Arab States,Bahla Fort +19.60638889,-100.2416667,Natural,Mexico,Latin America and the Caribbean,Monarch Butterfly Biosphere Reserve +22.60805556,-102.3791667,Cultural,Mexico,Latin America and the Caribbean,Camino Real de Tierra Adentro +16.95083333,-96.42111111,Cultural,Mexico,Latin America and the Caribbean,Prehistoric Caves of Yagul and Mitla in the Central Valley of Oaxaca +32,-113.9166667,Natural,Mexico,Latin America and the Caribbean,El Pinacate and Gran Desierto de Altar Biosphere Reserve +19.83527778,-98.66256667,Cultural,Mexico,Latin America and the Caribbean,Aqueduct of Padre Tembleque Hydraulic System +18.78805556,-110.9752778,Natural,Mexico,Latin America and the Caribbean,Archipiélago de Revillagigedo +17.98996111,-97.18715278,Mixed,Mexico,Latin America and the Caribbean,Tehuacán-Cuicatlán Valley: originary habitat of Mesoamerica +6.839722222,158.3308333,Cultural,Micronesia (Federated States of),Asia and the Pacific,Nan Madol: Ceremonial Centre of Eastern Micronesia +47.55666667,102.8313889,Cultural,Mongolia,Asia and the Pacific,Orkhon Valley Cultural Landscape +49.33388889,88.39527778,Cultural,Mongolia,Asia and the Pacific,Petroglyphic Complexes of the Mongolian Altai +48.76197778,109.0093278,Cultural,Mongolia,Asia and the Pacific,Great Burkhan Khaldun Mountain and its surrounding sacred landscape +43.133,19.0166,Natural,Montenegro,Europe and North America,Durmitor National Park +42.48333,18.7,Cultural,Montenegro,Europe and North America,Natural and Culturo-Historical Region of Kotor +34.06111,-4.97778,Cultural,Morocco,Arab States,Medina of Fez +31.63139,-7.98667,Cultural,Morocco,Arab States,Medina of Marrakesh +31.04722,-7.12889,Cultural,Morocco,Arab States,Ksar of Ait-Ben-Haddou +31.51411111,-9.770305556,Cultural,Morocco,Arab States,Medina of Essaouira (formerly Mogador) +33.88333,-5.55833,Cultural,Morocco,Arab States,Historic City of Meknes +34.07389,-5.55694,Cultural,Morocco,Arab States,Archaeological Site of Volubilis +35.57083,-5.36667,Cultural,Morocco,Arab States,Medina of Tétouan (formerly known as Titawin) +33.25667,-8.50194,Cultural,Morocco,Arab States,Portuguese City of Mazagan (El Jadida) +7.246925,134.3525,Mixed,Palau,Asia and the Pacific,Rock Islands Southern Lagoon +31.70435278,35.2075,Cultural,Palestine,Arab States,"Birthplace of Jesus: Church of the Nativity and the Pilgrimage Route, Bethlehem" +31.71972222,35.13055556,Cultural,Palestine,Arab States,"Palestine: Land of Olives and Vines ⿿ Cultural Landscape of Southern Jerusalem, Battir" +31.52416667,35.10888889,Cultural,Palestine,Arab States,Hebron/Al-Khalil Old Town +9.553888889,-79.65583333,Cultural,Panama,Latin America and the Caribbean,Fortifications on the Caribbean Side of Panama: Portobelo-San Lorenzo +7.736111111,-77.54722222,Natural,Panama,Latin America and the Caribbean,Darien National Park +8.951111111,-79.54055556,Cultural,Panama,Latin America and the Caribbean,Archaeological Site of Panamá Viejo and Historic District of Panamá +7.433,-81.766,Natural,Panama,Latin America and the Caribbean,Coiba National Park and its Special Zone of Marine Protection +-5.783711111,144.3317222,Cultural,Papua New Guinea,Asia and the Pacific,Kuk Early Agricultural Site +-27.13333,-55.7,Cultural,Paraguay,Latin America and the Caribbean,Jesuit Missions of La Santísima Trinidad de Paraná and Jesús de Tavarangue +-13.52222,-71.98333,Cultural,Peru,Latin America and the Caribbean,City of Cuzco +-13.11666667,-72.58333333,Mixed,Peru,Latin America and the Caribbean,Historic Sanctuary of Machu Picchu +-9.592772544,-77.1784535,Cultural,Peru,Latin America and the Caribbean,Chavin (Archaeological Site) +-9.33333,-77.4,Natural,Peru,Latin America and the Caribbean,Huascarán National Park +-8.1,-79.08333,Cultural,Peru,Latin America and the Caribbean,Chan Chan Archaeological Zone +14.59,120.97,Cultural,Philippines,Asia and the Pacific,Baroque Churches of the Philippines +16.93389,121.13667,Cultural,Philippines,Asia and the Pacific,Rice Terraces of the Philippine Cordilleras +6.717169444,126.1734306,Natural,Philippines,Asia and the Pacific,Mount Hamiguitan Range Wildlife Sanctuary +50.06138889,19.93722222,Cultural,Poland,Europe and North America,Historic Centre of Kraków +52.26639,21.01167,Cultural,Poland,Europe and North America,Historic Centre of Warsaw +50.03888889,19.175,Cultural,Poland,Europe and North America,Auschwitz Birkenau
German Nazi Concentration and Extermination Camp (1940-1945) +49.97916667,20.06388889,Cultural,Poland,Europe and North America,Wieliczka and Bochnia Royal Salt Mines +50.71667,23.26667,Cultural,Poland,Europe and North America,Old City of ZamoſĿ +53.01,18.61944,Cultural,Poland,Europe and North America,Medieval Town of Toruſ +51.10694722,17.07701389,Cultural,Poland,Europe and North America,Centennial Hall in Wrocſaw +50.44269722,18.85122778,Cultural,Poland,Europe and North America,Tarnowskie Góry Lead-Silver-Zinc Mine and its Underground Water Management System +50.96797222,21.50230556,Cultural,Poland,Europe and North America,Krzemionki Prehistoric Striped Flint Mining Region +38.655,-27.22,Cultural,Portugal,Europe and North America,Central Zone of the Town of Angra do Heroismo in the Azores +38.69194,-9.21583,Cultural,Portugal,Europe and North America,Monastery of the Hieronymites and Tower of Belém in Lisbon +39.65778,-8.82694,Cultural,Portugal,Europe and North America,Monastery of Batalha +39.60472,-8.4175,Cultural,Portugal,Europe and North America,Convent of Christ in Tomar +38.57306,-7.90778,Cultural,Portugal,Europe and North America,Historic Centre of ÿvora +39.55,-8.97667,Cultural,Portugal,Europe and North America,Monastery of Alcobaça +54.04166667,19.03333333,Cultural,Poland,Europe and North America,Castle of the Teutonic Order in Malbork +49.86666667,19.66666667,Cultural,Poland,Europe and North America,Kalwaria Zebrzydowska: the Mannerist Architectural and Park Landscape Complex and Pilgrimage Park +49.75,21.23333333,Cultural,Poland,Europe and North America,Wooden Churches of Southern Maſopolska +51.05427778,16.19594444,Cultural,Poland,Europe and North America,Churches of Peace in Jawor and ſwidnica +-12.25,-71.75,Natural,Peru,Latin America and the Caribbean,Manú National Park +-12.05139,-77.04306,Cultural,Peru,Latin America and the Caribbean,Historic Centre of Lima +-7.75,-77.25,Mixed,Peru,Latin America and the Caribbean,Río Abiseo National Park +-14.72583,-75.14861,Cultural,Peru,Latin America and the Caribbean,Lines and Geoglyphs of Nasca and Palpa +-16.4,-71.53333333,Cultural,Peru,Latin America and the Caribbean,Historical Centre of the City of Arequipa +-10.89166667,-77.52138889,Cultural,Peru,Latin America and the Caribbean,Sacred City of Caral-Supe +17.575,120.3875,Cultural,Philippines,Asia and the Pacific,Historic City of Vigan +10.16666667,118.9166667,Natural,Philippines,Asia and the Pacific,Puerto-Princesa Subterranean River National Park +8.953333333,119.8675,Natural,Philippines,Asia and the Pacific,Tubbataha Reefs Natural Park +23.26986,56.745,Cultural,Oman,Arab States,"Archaeological Sites of Bat, Al-Khutm and Al-Ayn" +18.25333,53.64759,Cultural,Oman,Arab States,Land of Frankincense +22.99888889,57.53605556,Cultural,Oman,Arab States,Aflaj Irrigation Systems of Oman +22.69522222,59.37811111,Cultural,Oman,Arab States,Ancient City of Qalhat +27.32916667,68.13888889,Cultural,Pakistan,Asia and the Pacific,Archaeological Ruins at Moenjodaro +33.77916667,72.8875,Cultural,Pakistan,Asia and the Pacific,Taxila +34.32083333,71.94583333,Cultural,Pakistan,Asia and the Pacific,Buddhist Ruins of Takht-i-Bahi and Neighbouring City Remains at Sahr-i-Bahlol +24.76666667,67.9,Cultural,Pakistan,Asia and the Pacific,"Historical Monuments at Makli, Thatta" +31.59027778,74.30972222,Cultural,Pakistan,Asia and the Pacific,Fort and Shalamar Gardens in Lahore +32.9625,73.58888889,Cultural,Pakistan,Asia and the Pacific,Rohtas Fort +37.19722222,128.4527778,Cultural,Republic of Korea,Asia and the Pacific,Royal Tombs of the Joseon Dynasty +36.53916667,128.5166667,Cultural,Republic of Korea,Asia and the Pacific,Historic Villages of Korea: Hahoe and Yangdong +37.47888889,127.1811111,Cultural,Republic of Korea,Asia and the Pacific,Namhansanseong +36.46194444,127.1272222,Cultural,Republic of Korea,Asia and the Pacific,Baekje Historic Areas +36.72729722,128.8434278,Cultural,Republic of Korea,Asia and the Pacific,"Seowon, Korean Neo-Confucian Academies" +36.54194444,127.8333333,Cultural,Republic of Korea,Asia and the Pacific,"Sansa, Buddhist Mountain Monasteries in Korea" +45.08333,29.5,Natural,Romania,Europe and North America,Danube Delta +46.13583333,24.77305556,Cultural,Romania,Europe and North America,Villages with Fortified Churches in Transylvania +45.18333333,24.01666667,Cultural,Romania,Europe and North America,Monastery of Horezu +47.77833333,25.71277778,Cultural,Romania,Europe and North America,Churches of Moldavia +46.21777778,24.79222222,Cultural,Romania,Europe and North America,Historic Centre of Sighiſoara +47.82083333,24.05583333,Cultural,Romania,Europe and North America,Wooden Churches of Maramureſ +45.62305556,23.31194444,Cultural,Romania,Europe and North America,Dacian Fortresses of the Orastie Mountains +59.95,30.31833,Cultural,Russian Federation,Europe and North America,Historic Centre of Saint Petersburg and Related Groups of Monuments +62.07138889,35.2275,Cultural,Russian Federation,Europe and North America,Kizhi Pogost +55.74583,37.62972,Cultural,Russian Federation,Europe and North America,"Kremlin and Red Square, Moscow" +58.53333,31.28333,Cultural,Russian Federation,Europe and North America,Historic Monuments of Novgorod and Surroundings +65.08333333,35.66666667,Cultural,Russian Federation,Europe and North America,Cultural and Historic Ensemble of the Solovetsky Islands +56.15,40.41666667,Cultural,Russian Federation,Europe and North America,White Monuments of Vladimir and Suzdal +55.65556,37.67389,Cultural,Russian Federation,Europe and North America,"Church of the Ascension, Kolomenskoye" +56.31035,38.1312,Cultural,Russian Federation,Europe and North America,Architectural Ensemble of the Trinity Sergius Lavra in Sergiev Posad +63.62583333,58.9525,Natural,Russian Federation,Europe and North America,Virgin Komi Forests +53.17361111,107.6625,Natural,Russian Federation,Europe and North America,Lake Baikal +56.33333333,158.5,Natural,Russian Federation,Europe and North America,Volcanoes of Kamchatka +46.68333333,136.6611111,Natural,Russian Federation,Europe and North America,Central Sikhote-Alin +50.46666667,86,Natural,Russian Federation,Europe and North America,Golden Mountains of Altai +44,40,Natural,Russian Federation,Europe and North America,Western Caucasus +55.79111111,49.095,Cultural,Russian Federation,Europe and North America,Historic and Architectural Complex of the Kazan Kremlin +54.97888889,49.05638889,Cultural,Russian Federation,Europe and North America,Bolgar Historical and Archaeological Complex +59.95,38.56666667,Cultural,Russian Federation,Europe and North America,Ensemble of the Ferapontov Monastery +38.78333,-9.41667,Cultural,Portugal,Europe and North America,Cultural Landscape of Sintra +41.14166667,-8.616666667,Cultural,Portugal,Europe and North America,"Historic Centre of Oporto, Luiz I Bridge and Monastery of Serra do Pilar " +32.76666667,-17,Natural,Portugal,Europe and North America,Laurisilva of Madeira +41.443731,-8.292803,Cultural,Portugal,Europe and North America,Historic Centre of Guimarães +41.10166667,-7.798888889,Cultural,Portugal,Europe and North America,Alto Douro Wine Region +38.51344444,-28.54116667,Cultural,Portugal,Europe and North America,Landscape of the Pico Island Vineyard Culture +38.88061944,-7.163322222,Cultural,Portugal,Europe and North America,Garrison Border Town of Elvas and its Fortifications +40.20781111,-8.425775,Cultural,Portugal,Europe and North America,University of Coimbra ⿿ Alta and Sofia +38.93716667,-9.325527778,Cultural,Portugal,Europe and North America,"Royal Building of Mafra ⿿ Palace, Basilica, Convent, Cerco Garden and Hunting Park (Tapada)" +41.55494444,-8.377027778,Cultural,Portugal,Europe and North America,Sanctuary of Bom Jesus do Monte in Braga +25.97805556,51.02972222,Cultural,Qatar,Arab States,Al Zubarah Archaeological Site +35.78333333,129.35,Cultural,Republic of Korea,Asia and the Pacific,Seokguram Grotto and Bulguksa Temple +35.8,128.1,Cultural,Republic of Korea,Asia and the Pacific,"Haeinsa Temple Janggyeong Panjeon, the Depositories for the Tripitaka Koreana Woodblocks" +37.55,126.98333,Cultural,Republic of Korea,Asia and the Pacific,Jongmyo Shrine +37.55,126.9833333,Cultural,Republic of Korea,Asia and the Pacific,Changdeokgung Palace Complex +37.27222,127.00833,Cultural,Republic of Korea,Asia and the Pacific,Hwaseong Fortress +35.78889,129.22667,Cultural,Republic of Korea,Asia and the Pacific,Gyeongju Historic Areas +34.96667,126.92917,Cultural,Republic of Korea,Asia and the Pacific,"Gochang, Hwasun and Ganghwa Dolmen Sites" +33.46888889,126.7202778,Natural,Republic of Korea,Asia and the Pacific,Jeju Volcanic Island and Lava Tubes +7.60318,-8.39097,Natural,"Côte d'Ivoire,Guinea",Africa,Mount Nimba Strict Nature Reserve +45.70333333,9.663611111,Cultural,"Croatia,Italy,Montenegro",Europe and North America,Venetian Works of Defence between the 16th and 17th Centuries: Stato da Terra ⿿ Western Stato da Mar +50.40652778,12.83734444,Cultural,"Czechia,Germany",Europe and North America,Erzgebirge/Krušnohoſí Mining Region +53.52861111,8.556111111,Natural,"Denmark,Germany,Netherlands",Europe and North America,Wadden Sea +63.3,21.3,Natural,"Finland,Sweden",Europe and North America,High Coast / Kvarken Archipelago +42.68542,-0.0005,Mixed,"France,Spain",Europe and North America,Pyrénées - Mont Perdu +25.40216667,49.63056944,Cultural,Saudi Arabia,Arab States,"Al-Ahsa Oasis, an Evolving Cultural Landscape" +16.5,-16.16667,Natural,Senegal,Africa,Djoudj National Bird Sanctuary +14.66722,-17.40083,Cultural,Senegal,Africa,Island of Gorée +13.06667,-12.71667,Natural,Senegal,Africa,Niokolo-Koba National Park +16.02778,-16.50444,Cultural,Senegal,Africa,Island of Saint-Louis +13.83527778,-16.49861111,Cultural,Senegal,Africa,Saloum Delta +12.59333333,-12.84583333,Cultural,Senegal,Africa,"Bassari Country: Bassari, Fula and Bedik Cultural Landscapes" +43.11889,20.42278,Cultural,Serbia,Europe and North America,Stari Ras and SopoĿani +43.48611,20.53667,Cultural,Serbia,Europe and North America,Studenica Monastery +42.66111111,20.26555556,Cultural,Serbia,Europe and North America,Medieval Monuments in Kosovo +36.94639,11.09917,Cultural,Tunisia,Arab States,Punic Town of Kerkuane and its Necropolis +35.82778,10.63861,Cultural,Tunisia,Arab States,Medina of Sousse +35.68167,10.10389,Cultural,Tunisia,Arab States,Kairouan +36.42361,9.22028,Cultural,Tunisia,Arab States,Dougga / Thugga +41.00847,28.97993,Cultural,Turkey,Europe and North America,Historic Areas of Istanbul +38.66667,34.85,Mixed,Turkey,Europe and North America,Göreme National Park and the Rock Sites of Cappadocia +39.371271,38.121826,Cultural,Turkey,Europe and North America,Great Mosque and Hospital of DivriĿi +40.01389,34.62056,Cultural,Turkey,Europe and North America,Hattusha: the Hittite Capital +38.03661,38.76369,Cultural,Turkey,Europe and North America,Nemrut DaĿ +36.335,29.32028,Cultural,Turkey,Europe and North America,Xanthos-Letoon +37.92389,29.12333,Mixed,Turkey,Europe and North America,Hierapolis-Pamukkale +41.26,32.68972,Cultural,Turkey,Europe and North America,City of Safranbolu +43.89930556,22.18611111,Cultural,Serbia,Europe and North America,"Gamzigrad-Romuliana, Palace of Galerius" +-9.416666667,46.41666667,Natural,Seychelles,Africa,Aldabra Atoll +-4.32917,55.7375,Natural,Seychelles,Africa,Vallée de Mai Nature Reserve +1.315277778,103.8161111,Cultural,Singapore,Asia and the Pacific,Singapore Botanic Gardens +48.46111,18.9,Cultural,Slovakia,Europe and North America,Historic Town of Banská Štiavnica and the Technical Monuments in its Vicinity +48.99944444,20.7675,Cultural,Slovakia,Europe and North America,"Levoča, Spišský Hrad and the Associated Cultural Monuments" +49.03916667,19.27833333,Cultural,Slovakia,Europe and North America,Vlkolínec +49.29277778,21.27555556,Cultural,Slovakia,Europe and North America,Bardejov Town Conservation Reserve +49.33611111,19.55833333,Cultural,Slovakia,Europe and North America,Wooden Churches of the Slovak part of the Carpathian Mountain Area +45.66667,14,Natural,Slovenia,Europe and North America,Škocjan Caves +39.95644,26.239,Cultural,Turkey,Europe and North America,Archaeological Site of Troy +37.92916667,27.35944444,Cultural,Turkey,Europe and North America,Ephesus +41.67777778,26.55944444,Cultural,Turkey,Europe and North America,Selimiye Mosque and its Social Complex +37.66666667,32.82805556,Cultural,Turkey,Europe and North America,Neolithic Site of ÿatalhöyük +40.18473056,29.06233611,Cultural,Turkey,Europe and North America,Bursa and Cumalıkızık: the Birth of the Ottoman Empire +39.12583333,27.18,Cultural,Turkey,Europe and North America,Pergamon and its Multi-Layered Cultural Landscape +37.9031,40.23930833,Cultural,Turkey,Europe and North America,Diyarbakır Fortress and Hevsel Gardens Cultural Landscape +-11.68333,160.33333,Natural,Solomon Islands,Asia and the Pacific,East Rennell +-27.83889,32.55,Natural,South Africa,Africa,iSimangaliso Wetland Park +-24.15861,29.17694,Cultural,South Africa,Africa,Fossil Hominid Sites of South Africa +-33.8,18.36666667,Cultural,South Africa,Africa,Robben Island +-34.36111111,18.475,Natural,South Africa,Africa,Cape Floral Region Protected Areas +40.5,43.56666667,Cultural,Turkey,Europe and North America,Archaeological Site of Ani +37.70833333,28.72361111,Cultural,Turkey,Europe and North America,Aphrodisias +37.22324194,38.92236389,Cultural,Turkey,Europe and North America,Göbekli Tepe +37.70083,62.1775,Cultural,Turkmenistan,Asia and the Pacific,State Historical and Cultural Park ⿿Ancient Merv⿝ +42.18318,59.08494,Cultural,Turkmenistan,Asia and the Pacific,Kunya-Urgench +37.99972222,58.19861111,Cultural,Turkmenistan,Asia and the Pacific,Parthian Fortresses of Nisa +-1.080555556,29.66138889,Natural,Uganda,Africa,Bwindi Impenetrable National Park +0.223611111,29.92416667,Natural,Uganda,Africa,Rwenzori Mountains National Park +37.74611111,-119.5966667,Natural,United States of America,Europe and North America,Yosemite National Park +-22.1925,29.23889,Cultural,South Africa,Africa,Mapungubwe Cultural Landscape +-26.86,27.26,Natural,South Africa,Africa,Vredefort Dome +-28.6,17.20388889,Cultural,South Africa,Africa,Richtersveld Cultural and Botanical Landscape +-25.68761111,20.37458333,Cultural,South Africa,Africa,ǿKhomani Cultural Landscape +-25.97388889,31.01388889,Natural,South Africa,Africa,Barberton Makhonjwa Mountains +43.38252778,-4.116166667,Cultural,Spain,Europe and North America,Cave of Altamira and Paleolithic Cave Art of Northern Spain +0.348611111,32.55138889,Cultural,Uganda,Africa,Tombs of Buganda Kings at Kasubi +50.45258,30.51686,Cultural,Ukraine,Europe and North America,"Kiev: Saint-Sophia Cathedral and Related Monastic Buildings, Kiev-Pechersk Lavra" +49.84163,24.03198,Cultural,Ukraine,Europe and North America,L'viv ⿿ the Ensemble of the Historic Centre +48.29666667,25.92472222,Cultural,Ukraine,Europe and North America,Residence of Bukovinian and Dalmatian Metropolitans +44.61083333,33.49138889,Cultural,Ukraine,Europe and North America,Ancient City of Tauric Chersonese and its Chora +24.06777778,55.80638889,Cultural,United Arab Emirates,Arab States,"Cultural Sites of Al Ain (Hafit, Hili, Bidaa Bint Saud and Oases Areas)" +55.25,-6.485277778,Natural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Giant's Causeway and Causeway Coast +40.94847222,-4.11675,Cultural,Spain,Europe and North America,Old Town of Segovia and its Aqueduct +43.36262,-5.84303,Cultural,Spain,Europe and North America,Monuments of Oviedo and the Kingdom of the Asturias +37.87919444,-4.779722222,Cultural,Spain,Europe and North America,Historic Centre of Cordoba +37.17667,-3.59444,Cultural,Spain,Europe and North America,"Alhambra, Generalife and Albayzín, Granada" +42.34073333,-3.704011111,Cultural,Spain,Europe and North America,Burgos Cathedral +40.58175,-4.126416667,Cultural,Spain,Europe and North America,"Monastery and Site of the Escurial, Madrid" +41.41338,2.152972,Cultural,Spain,Europe and North America,Works of Antoni Gaudí +42.88076,-8.54468,Cultural,Spain,Europe and North America,Santiago de Compostela (Old Town) +40.65645,-4.70012,Cultural,Spain,Europe and North America,Old Town of Ávila with its Extra-Muros Churches +54.77472222,-1.576111111,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Durham Castle and Cathedral +52.62638889,-2.472777778,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Ironbridge Gorge +54.11611111,-1.573055556,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Studley Royal Park including the Ruins of Fountains Abbey +51.17888889,-1.825277778,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,"Stonehenge, Avebury and Associated Sites" +53.13972222,-4.276944444,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Castles and Town Walls of King Edward in Gwynedd +57.81722222,-8.576666667,Mixed,United Kingdom of Great Britain and Northern Ireland,Europe and North America,St Kilda +54.47661111,-3.082416667,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,The English Lake District +51.84194444,-1.361388889,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Blenheim Palace +51.49972222,-0.128611111,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Palace of Westminster and Westminster Abbey including Saint Margaret⿿s Church +51.38138889,-2.358611111,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,City of Bath +13.69111111,-15.5225,Cultural,"Gambia (the),Senegal",Africa,Stone Circles of Senegambia +51.57930556,14.72644444,Cultural,"Germany,Poland",Europe and North America,Muskauer Park / Park Mużakowski +41.89022222,12.49230556,Cultural,"Holy See,Italy",Europe and North America,"Historic Centre of Rome, the Properties of the Holy See in that City Enjoying Extraterritorial Rights and San Paolo Fuori le Mura" +48.47573,20.48687,Natural,"Hungary,Slovakia",Europe and North America,Caves of Aggtelek Karst and Slovak Karst +45.88888889,8.913888889,Natural,"Italy,Switzerland",Europe and North America,Monte San Giorgio +46.49833333,9.846388889,Cultural,"Italy,Switzerland",Europe and North America,Rhaetian Railway in the Albula / Bernina Landscapes +40.34389,-1.10722,Cultural,Spain,Europe and North America,Mudejar Architecture of Aragon +39.86688889,-4.029416667,Cultural,Spain,Europe and North America,Historic City of Toledo +28.12625,-17.23722222,Natural,Spain,Europe and North America,Garajonay National Park +40.96525,-5.6645,Cultural,Spain,Europe and North America,Old City of Salamanca +37.38384,-5.99155,Cultural,Spain,Europe and North America,"Cathedral, Alcázar and Archivo de Indias in Seville" +39.47444,-6.37,Cultural,Spain,Europe and North America,Old Town of Cáceres +38.91113889,1.435194444,Mixed,Spain,Europe and North America,"Ibiza, Biodiversity and Culture" +41.38083,1.0825,Cultural,Spain,Europe and North America,Poblet Monastery +55.66333333,-3.783055556,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,New Lanark +-24.36666667,-128.3333333,Natural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Henderson Island +51.50805556,-0.076111111,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Tower of London +51.28,1.083333333,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,"Canterbury Cathedral, St Augustine's Abbey, and St Martin's Church" +58.99605556,-3.188666667,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Heart of Neolithic Orkney +55.95,-3.216666667,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Old and New Towns of Edinburgh +-40.32472222,-9.928611111,Natural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Gough and Inaccessible Islands +51.48116667,-0.003777778,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Maritime Greenwich +0,0,Natural,"Kazakhstan,Kyrgyzstan,Uzbekistan",Asia and the Pacific,Western Tien-Shan +-29.76527778,29.12305556,Mixed,"Lesotho,South Africa",Africa,Maloti-Drakensberg Park +55.27458,20.96239,Cultural,"Lithuania,Russian Federation",Europe and North America,Curonian Spit +49.53388889,21.03222222,Cultural,"Poland,Ukraine",Europe and North America,Wooden Tserkvas of the Carpathian Region in Poland and Ukraine +40.6975,-6.661111111,Cultural,"Portugal,Spain",Europe and North America,Prehistoric Rock Art Sites in the Côa Valley and Siega Verde +38.01131,-3.37122,Cultural,Spain,Europe and North America,Renaissance Monumental Ensembles of ÿbeda and Baeza +38.91611,-6.33778,Cultural,Spain,Europe and North America,Archaeological Ensemble of Mérida +39.45285,-5.3275,Cultural,Spain,Europe and North America,Royal Monastery of Santa María de Guadalupe +43.335,-6.414722222,Cultural,Spain,Europe and North America,Routes of Santiago de Compostela: Camino Francés and Routes of Northern Spain +36.9477,-6.358861,Natural,Spain,Europe and North America,Doñana National Park +40.07662,-2.13174,Cultural,Spain,Europe and North America,Historic Walled Town of Cuenca +39.47441667,-0.378444444,Cultural,Spain,Europe and North America,La Lonja de la Seda de Valencia +42.46939,-6.77075,Cultural,Spain,Europe and North America,Las Médulas +32.37944444,-64.67777778,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,"Historic Town of St George and Related Fortifications, Bermuda" +51.77638889,-3.088055556,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Blaenavon Industrial Landscape +53.83916667,-1.788333333,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Saltaire +50.70555556,-2.989888889,Natural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Dorset and East Devon Coast +53.02888889,-1.488055556,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Derwent Valley Mills +51.48194444,-0.294027778,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,"Royal Botanic Gardens, Kew" +50.275,92.71972222,Natural,"Russian Federation,Mongolia",Europe and North America,Uvs Nuur Basin +49.93022222,115.4254444,Natural,"Russian Federation,Mongolia",Europe and North America,Landscapes of Dauria +38.77527778,-4.838888889,Cultural,"Slovenia,Spain",Europe and North America,Heritage of Mercury. Almadén and Idrija +-17.92453,25.85539,Natural,"Zambia,Zimbabwe",Africa,Mosi-oa-Tunya / Victoria Falls +41.38778,2.175,Cultural,Spain,Europe and North America,"Palau de la Música Catalana and Hospital de Sant Pau, Barcelona" +42.32586,-2.86496,Cultural,Spain,Europe and North America,San Millán Yuso and Suso Monasteries +39.78995,-1.03331,Cultural,Spain,Europe and North America,Rock Art of the Mediterranean Basin on the Iberian Peninsula +41.11472222,1.259305556,Cultural,Spain,Europe and North America,Archaeological Ensemble of Tárraco +40.48138889,-3.368055556,Cultural,Spain,Europe and North America,University and Historic Precinct of Alcalá de Henares +28.47788889,-16.31177778,Cultural,Spain,Europe and North America,San Cristóbal de La Laguna +53.40666667,-2.994444444,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Liverpool ⿿ Maritime Mercantile City +50.13611111,-5.383611111,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Cornwall and West Devon Mining Landscape +52.97027778,-3.087777778,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Pontcysyllte Aqueduct and Canal +56.00111111,-3.388888889,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,The Forth Bridge +36.12266944,-5.342061111,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Gorham's Cave Complex +53.23391667,-2.303861111,Cultural,United Kingdom of Great Britain and Northern Ireland,Europe and North America,Jodrell Bank Observatory +38.26666667,-0.716666667,Cultural,Spain,Europe and North America,Palmeral of Elche +43.01111,-7.55333,Cultural,Spain,Europe and North America,Roman Walls of Lugo +42.50472222,0.803611111,Cultural,Spain,Europe and North America,Catalan Romanesque Churches of the Vall de Boí +42.37138889,-3.547222222,Cultural,Spain,Europe and North America,Archaeological Site of Atapuerca +40.03645,-3.60934,Cultural,Spain,Europe and North America,Aranjuez Cultural Landscape +43.323175,-3.016833333,Cultural,Spain,Europe and North America,Vizcaya Bridge +28.27138889,-16.64361111,Natural,Spain,Europe and North America,Teide National Park +-3.18722,35.54083,Mixed,United Republic of Tanzania,Africa,Ngorongoro Conservation Area +-8.95778,39.52278,Cultural,United Republic of Tanzania,Africa,Ruins of Kilwa Kisiwani and Ruins of Songo Mnara +-2.33333,34.56667,Natural,United Republic of Tanzania,Africa,Serengeti National Park +-6.16306,39.18917,Cultural,United Republic of Tanzania,Africa,Stone Town of Zanzibar +-9,37.4,Natural,United Republic of Tanzania,Africa,Selous Game Reserve +-3.06667,37.36667,Natural,United Republic of Tanzania,Africa,Kilimanjaro National Park +-4.724444444,35.83388889,Cultural,United Republic of Tanzania,Africa,Kondoa Rock-Art Sites +37.26166667,-108.4855556,Cultural,United States of America,Europe and North America,Mesa Verde National Park +44.46056,-110.82778,Natural,United States of America,Europe and North America,Yellowstone National Park +36.10083333,-112.0905556,Natural,United States of America,Europe and North America,Grand Canyon National Park +43.38583333,-8.406388889,Cultural,Spain,Europe and North America,Tower of Hercules +39.73083333,2.694722222,Cultural,Spain,Europe and North America,Cultural Landscape of the Serra de Tramuntana +37.025,-4.544444444,Cultural,Spain,Europe and North America,Antequera Dolmens Site +37.88588889,-4.867694444,Cultural,Spain,Europe and North America,Caliphate City of Medina Azahara +28.04438889,-15.66119444,Cultural,Spain,Europe and North America,Risco Caido and the Sacred Mountains of Gran Canaria Cultural Landscape +8.333333333,80.38333333,Cultural,Sri Lanka,Asia and the Pacific,Sacred City of Anuradhapura +7.915833333,81.00055556,Cultural,Sri Lanka,Asia and the Pacific,Ancient City of Polonnaruwa +7.95,80.75,Cultural,Sri Lanka,Asia and the Pacific,Ancient City of Sigiriya +6.416666667,80.5,Natural,Sri Lanka,Asia and the Pacific,Sinharaja Forest Reserve +7.293611111,80.64027778,Cultural,Sri Lanka,Asia and the Pacific,Sacred City of Kandy +25.55444444,-80.99638889,Natural,United States of America,Europe and North America,Everglades National Park +39.94861111,-75.15,Cultural,United States of America,Europe and North America,Independence Hall +41.37388889,-123.9980556,Natural,United States of America,Europe and North America,Redwood National and State Parks +37.18722222,-86.10305556,Natural,United States of America,Europe and North America,Mammoth Cave National Park +47.74833333,-123.4488889,Natural,United States of America,Europe and North America,Olympic National Park +38.65861111,-90.06138889,Cultural,United States of America,Europe and North America,Cahokia Mounds State Historic Site +35.59305556,-83.43555556,Natural,United States of America,Europe and North America,Great Smoky Mountains National Park +18.46666667,-66.125,Cultural,United States of America,Europe and North America,La Fortaleza and San Juan National Historic Site in Puerto Rico +40.68944444,-74.04472222,Cultural,United States of America,Europe and North America,Statue of Liberty +36.06377778,-107.9708333,Cultural,United States of America,Europe and North America,Chaco Culture +19.40083333,-155.1236111,Natural,United States of America,Europe and North America,Hawaii Volcanoes National Park +6.021388889,80.21861111,Cultural,Sri Lanka,Asia and the Pacific,Old Town of Galle and its Fortifications +7.856666667,80.64916667,Cultural,Sri Lanka,Asia and the Pacific,Golden Temple of Dambulla +7.45245,80.8021,Natural,Sri Lanka,Asia and the Pacific,Central Highlands of Sri Lanka +19.73611111,37.44305556,Natural,Sudan,Arab States,Sanganeb Marine National Park and Dungonab Bay ⿿ Mukkawar Island Marine National Park +18.537,31.82802778,Cultural,Sudan,Arab States,Gebel Barkal and the Sites of the Napatan Region +16.93333333,33.71666667,Cultural,Sudan,Arab States,Archaeological Sites of the Island of Meroe +5.82611,-55.15,Cultural,Suriname,Latin America and the Caribbean,Historic Inner City of Paramaribo +38.03277778,-78.50388889,Cultural,United States of America,Europe and North America,Monticello and the University of Virginia in Charlottesville +36.43889,-105.54167,Cultural,United States of America,Europe and North America,Taos Pueblo +32.16666667,-104.3833333,Natural,United States of America,Europe and North America,Carlsbad Caverns National Park +32.63694444,-91.40638889,Cultural,United States of America,Europe and North America,Monumental Earthworks of Poverty Point +29.32805556,-98.46,Cultural,United States of America,Europe and North America,San Antonio Missions +39.90557083,-79.46647556,Cultural,United States of America,Europe and North America,The 20th-Century Architecture of Frank Lloyd Wright +-34.46777778,-57.85333333,Cultural,Uruguay,Latin America and the Caribbean,Historic Quarter of the City of Colonia del Sacramento +-33.11777778,-58.33166667,Cultural,Uruguay,Latin America and the Caribbean,Fray Bentos Industrial Landscape +4,-56.5,Natural,Suriname,Latin America and the Caribbean,Central Suriname Nature Reserve +59.33514,17.54264,Cultural,Sweden,Europe and North America,Birka and Hovgården +59.96667,16.00833,Cultural,Sweden,Europe and North America,Engelsberg Ironworks +58.70111,11.34111,Cultural,Sweden,Europe and North America,Rock Carvings in Tanum +59.27556,18.09944,Cultural,Sweden,Europe and North America,Skogskyrkogården +59.32306,17.88333,Cultural,Sweden,Europe and North America,Royal Domain of Drottningholm +57.64167,18.29583,Cultural,Sweden,Europe and North America,Hanseatic Town of Visby +65.64611,22.02861,Cultural,Sweden,Europe and North America,"Church Town of Gammelstad, Luleå " +67.33333,17.58333,Mixed,Sweden,Europe and North America,Laponian Area +41.37833,60.36389,Cultural,Uzbekistan,Asia and the Pacific,Itchan Kala +39.77472,64.42861,Cultural,Uzbekistan,Asia and the Pacific,Historic Centre of Bukhara +39.66861,67,Cultural,Uzbekistan,Asia and the Pacific,Samarkand ⿿ Crossroad of Cultures +39.05,66.83333,Cultural,Uzbekistan,Asia and the Pacific,Historic Centre of Shakhrisyabz +-17.62806944,168.1777194,Cultural,Vanuatu,Asia and the Pacific,Chief Roi Mata⿿s Domain +11.4,-69.68333333,Cultural,Venezuela (Bolivarian Republic of),Latin America and the Caribbean,Coro and its Port +5.33333,-61.5,Natural,Venezuela (Bolivarian Republic of),Latin America and the Caribbean,Canaima National Park +10.49073,-66.89068,Cultural,Venezuela (Bolivarian Republic of),Latin America and the Caribbean,Ciudad Universitaria de Caracas +20.9,107.1,Natural,Viet Nam,Asia and the Pacific,Ha Long Bay +56.16667,15.58333,Cultural,Sweden,Europe and North America,Naval Port of Karlskrona +56.325,16.48333,Cultural,Sweden,Europe and North America,Agricultural Landscape of Southern ÿland +60.60472,15.63083,Cultural,Sweden,Europe and North America,Mining Area of the Great Copper Mountain in Falun +57.1,12.38333333,Cultural,Sweden,Europe and North America,"Grimeton Radio Station, Varberg" +61.70722222,16.19583333,Cultural,Sweden,Europe and North America,Decorated Farmhouses of Hälsingland +16.46944,107.57778,Cultural,Viet Nam,Asia and the Pacific,Complex of Hué Monuments +15.88333333,108.3333333,Cultural,Viet Nam,Asia and the Pacific,Hoi An Ancient Town +15.51666667,108.5666667,Cultural,Viet Nam,Asia and the Pacific,My Son Sanctuary +17.53722222,106.15125,Natural,Viet Nam,Asia and the Pacific,Phong Nha-Ke Bang National Park +21.03944444,105.8372222,Cultural,Viet Nam,Asia and the Pacific,Central Sector of the Imperial Citadel of Thang Long - Hanoi +20.07805556,105.6047222,Cultural,Viet Nam,Asia and the Pacific,Citadel of the Ho Dynasty +20.25666667,105.8963889,Mixed,Viet Nam,Asia and the Pacific,Trang An Landscape Complex +15.92694,48.62667,Cultural,Yemen,Arab States,Old Walled City of Shibam +15.35555556,44.20805556,Cultural,Yemen,Arab States,Old City of Sana'a +14.19533333,43.31552778,Cultural,Yemen,Arab States,Historic Town of Zabid +71.18888889,-179.7152778,Natural,Russian Federation,Europe and North America,Natural System of Wrangel Island Reserve +42.05297222,48.29719444,Cultural,Russian Federation,Europe and North America,"Citadel, Ancient City and Fortress Buildings of Derbent" +55.72611111,37.55508333,Cultural,Russian Federation,Europe and North America,Ensemble of the Novodevichy Convent +46.94806,7.45028,Cultural,Switzerland,Europe and North America,Old City of Berne +47.42333,9.37778,Cultural,Switzerland,Europe and North America,Abbey of St Gall +46.62945,10.44765,Cultural,Switzerland,Europe and North America,Benedictine Convent of St John at Müstair +46.19314,9.02242,Cultural,Switzerland,Europe and North America,"Three Castles, Defensive Wall and Ramparts of the Market-Town of Bellinzona" +46.5,8.033333333,Natural,Switzerland,Europe and North America,Swiss Alps Jungfrau-Aletsch +46.91666667,9.25,Natural,Switzerland,Europe and North America,Swiss Tectonic Arena Sardona +46.49194444,6.746111111,Cultural,Switzerland,Europe and North America,"Lavaux, Vineyard Terraces" +47.10388889,6.832777778,Cultural,Switzerland,Europe and North America,"La Chaux-de-Fonds / Le Locle, Watchmaking Town Planning" +12.5,53.83333333,Natural,Yemen,Arab States,Socotra Archipelago +-15.81944444,29.40805556,Natural,Zimbabwe,Africa,"Mana Pools National Park, Sapi and Chewore Safari Areas" +-20.5,28.5,Cultural,Zimbabwe,Africa,Matobo Hills +-20.28333333,30.93333333,Cultural,Zimbabwe,Africa,Great Zimbabwe National Monument +-20.15833333,28.37666667,Cultural,Zimbabwe,Africa,Khami Ruins National Monument +41.06925,20.64605556,Mixed,Albania,Europe and North America,Natural and Cultural Heritage of the Ohrid region +57.65278,39.87611,Cultural,Russian Federation,Europe and North America,Historical Centre of the City of Yaroslavl +69.04694444,94.15805556,Natural,Russian Federation,Europe and North America,Putorana Plateau +60.66666667,127,Natural,Russian Federation,Europe and North America,Lena Pillars Nature Park +57.80719444,28.32855556,Cultural,Russian Federation,Europe and North America,Churches of the Pskov School of Architecture +55.77027778,48.65277778,Cultural,Russian Federation,Europe and North America,Assumption Cathedral and Monastery of the town-island of Sviyazhsk +17.34694,-62.83722,Cultural,Saint Kitts and Nevis,Latin America and the Caribbean,Brimstone Hill Fortress National Park +33.51139,36.30639,Cultural,Syrian Arab Republic,Arab States,Ancient City of Damascus +36.19916667,37.16277778,Cultural,Syrian Arab Republic,Arab States,Ancient City of Aleppo +32.51806,36.48167,Cultural,Syrian Arab Republic,Arab States,Ancient City of Bosra +34.55417,38.26667,Cultural,Syrian Arab Republic,Arab States,Site of Palmyra +34.78166667,36.26305556,Cultural,Syrian Arab Republic,Arab States,Crac des Chevaliers and Qal⿿at Salah El-Din +36.33416667,36.84416667,Cultural,Syrian Arab Republic,Arab States,Ancient Villages of Northern Syria +39.50777778,67.46027778,Cultural,Tajikistan,Asia and the Pacific,Proto-urban Site of Sarazm +38.765,72.30527778,Natural,Tajikistan,Asia and the Pacific,Tajik National Park (Mountains of the Pamirs) +17.00722,99.78972,Cultural,Thailand,Asia and the Pacific,Historic Town of Sukhothai and Associated Historic Towns +-28.54333333,-54.26583333,Cultural,"Argentina,Brazil",Latin America and the Caribbean,"Jesuit Missions of the Guaranis: San Ignacio Mini, Santa Ana, " +47.71927778,16.72272222,Cultural,"Austria,Hungary",Europe and North America,Fertö / Neusiedlersee Cultural Landscape +52.7275,23.98111111,Natural,"Belarus,Poland",Europe and North America,Bialowieza Forest +50.17444,3.23139,Cultural,"Belgium,France",Europe and North America,Belfries of Belgium and France +13.80708333,-61.07036111,Natural,Saint Lucia,Latin America and the Caribbean,Pitons Management Area +43.93277778,12.45194444,Cultural,San Marino,Europe and North America,San Marino Historic Centre and Mount Titano +26.78361111,37.955,Cultural,Saudi Arabia,Arab States,Al-Hijr Archaeological Site (Madâin Sâlih) +24.73413333,46.57246667,Cultural,Saudi Arabia,Arab States,At-Turaif District in ad-Dir'iyah +21.48388889,39.1875,Cultural,Saudi Arabia,Arab States,"Historic Jeddah, the Gate to Makkah" +28.01055556,40.91305556,Cultural,Saudi Arabia,Arab States,Rock Art in the Hail Region of Saudi Arabia +17.54861,103.35833,Cultural,Thailand,Asia and the Pacific,Ban Chiang Archaeological Site +14.34778,100.56056,Cultural,Thailand,Asia and the Pacific,Historic City of Ayutthaya +14.33,102.05,Natural,Thailand,Asia and the Pacific,Dong Phayayen-Khao Yai Forest Complex +15.33333,98.91667,Natural,Thailand,Asia and the Pacific,Thungyai-Huai Kha Khaeng Wildlife Sanctuaries +10.06666667,1.133333333,Cultural,Togo,Africa,"Koutammakou, the Land of the Batammariba" +37.16361,9.67472,Natural,Tunisia,Arab States,Ichkeul National Park +36.81667,10.16667,Cultural,Tunisia,Arab States,Medina of Tunis +36.85278,10.32333,Cultural,Tunisia,Arab States,Archaeological Site of Carthage +35.29639,10.70694,Cultural,Tunisia,Arab States,Amphitheatre of El Jem +11.88416667,2.487777778,Natural,"Benin,Burkina Faso,Niger",Africa,W-Arly-Pendjari Complex +2.609444444,16.55416667,Natural,"Cameroon,Central African Republic,Congo",Africa,Sangha Trinational +61.19758333,-140.9919722,Natural,Canada,Europe and North America,Kluane / Wrangell-St. Elias / Glacier Bay / Tatshenshini-Alsek +48.99605556,-113.9041667,Natural,Canada,Europe and North America,Waterton Glacier International Peace Park +34.30444444,108.8572222,Cultural,"China,Kazakhstan,Kyrgyzstan",Asia and the Pacific,Silk Roads: the Routes Network of Chang'an-Tianshan Corridor +9.407083333,-82.93880556,Natural,"Costa Rica,Panama",Latin America and the Caribbean,Talamanca Range-La Amistad Reserves / La Amistad National Park \ No newline at end of file diff --git a/notebooks/dbsearch-with-selectai/unesco-sites-selectai-colab.ipynb b/notebooks/dbsearch-with-selectai/unesco-sites-selectai-colab.ipynb new file mode 100644 index 00000000..2974e99d --- /dev/null +++ b/notebooks/dbsearch-with-selectai/unesco-sites-selectai-colab.ipynb @@ -0,0 +1,3856 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "BQLdye_8UEVK" + }, + "source": [ + "\"Open\n", + "\n", + "# Oracle SelectAI with UNESCO World Heritage Sites - Google Colab Notebook\n", + "\n", + "This notebook demonstrates how to connect to Oracle Autonomous Database using wallet-based authentication and execute SelectAI queries with Cohere integration in Google Colab, specifically focused on UNESCO World Heritage Sites.\n", + "\n", + "**Prerequisites**:\n", + "- Oracle Autonomous Database instance\n", + "- Wallet files downloaded from Oracle Cloud Console in to Google Drive\n", + "- Generate Cohere API key\n", + "- Google Drive account for storing wallet files\n", + "- UNESCO_SITES table with World Heritage data ([Sample Data Table](https://github.com/madhusudhanrao-ppm/code-assets/blob/main/AI-for-Travel/UNESCO_SITES_sample_data.csv))\n", + "- Create Table UNESCO_SITES\n", + "- For more instructions please refer README.md file\n", + "\n", + "Date Updated: 21st Jan 2026" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "epX9hqD-UEVL" + }, + "source": [ + "## Section 1: Install Required Libraries and Dependencies\n", + "\n", + "Install the necessary packages including oracledb for Oracle database connectivity in Google Colab." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "OZDDDaJRUEVL", + "outputId": "394849f5-2ae8-4609-a3f6-9e17d2030b1b" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.4/2.4 MB\u001b[0m \u001b[31m16.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h✓ oracledb and visualization libraries installed successfully\n" + ] + } + ], + "source": [ + "!pip install oracledb pandas folium geopandas -q\n", + "import oracledb\n", + "import pandas as pd\n", + "import os\n", + "import json\n", + "from pathlib import Path\n", + "import folium\n", + "from folium import plugins\n", + "\n", + "print(\"✓ oracledb and visualization libraries installed successfully\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qkmq9JzjUEVM" + }, + "source": [ + "## Section 2: Download and Configure Oracle Wallet\n", + "\n", + "Mount Google Drive and download your Oracle wallet files. Upload your wallet.zip file to Google Drive first." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QNJc41pNUEVM", + "outputId": "d215af1b-e829-454e-c90a-9e194088aacf" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mounted at /content/drive\n", + "✓ Google Drive mounted successfully\n" + ] + } + ], + "source": [ + "from google.colab import drive\n", + "\n", + "# Mount Google Drive\n", + "drive.mount('/content/drive')\n", + "\n", + "print(\"✓ Google Drive mounted successfully\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Glb88uehUEVM" + }, + "source": [ + "## Section 3: Establish Connection to Oracle Autonomous Database\n", + "\n", + "Create and test the database connection using wallet-based authentication." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qhSqmQzaUEVM", + "outputId": "b699352c-d230-40db-c89a-dc541bd3c9ce" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "✓ Successfully connected to Oracle Database as ADMIN user\n", + "✓ UNESCO_SITES table contains 1113 records\n" + ] + } + ], + "source": [ + "# Connection parameters\n", + "username = \"DEMOUSER\" # Update with your username\n", + "adminusername = \"ADMIN\" # Update with your admin username\n", + "password = \"YourPassword\" # Update with your password\n", + "tns_name = \"indeducation_high\" # Update with your TNS alias from tnsnames.ora\n", + "wall_config_dir = \"/content/drive/MyDrive/Wallet_YourFolder\" # Update with your wallet path\n", + "wall_pwd = \"YourWalletPassword\" # Update with your wallet password\n", + "\n", + "try:\n", + " # Connect to Oracle Database\n", + " conn = oracledb.connect(user=adminusername,\n", + " password=password,\n", + " dsn=tns_name,\n", + " config_dir=wall_config_dir,\n", + " wallet_location=wall_config_dir,\n", + " wallet_password=wall_pwd)\n", + "\n", + " print(\"✓ Successfully connected to Oracle Database as ADMIN user\")\n", + "\n", + " # Test connection with a simple query\n", + " cursor = conn.cursor()\n", + " cursor.execute(\"SELECT COUNT(*) FROM DEMOUSER.UNESCO_SITES\")\n", + " count = cursor.fetchone()[0]\n", + " print(f\"✓ UNESCO_SITES table contains {count} records\")\n", + "\n", + "except Exception as e:\n", + " print(f\"✗ Connection failed: {e}\")\n", + " conn = None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hjw6Pv3TUEVN" + }, + "source": [ + "## Section 4: Grant Privileges and Setup SelectAI (Admin Tasks)\n", + "\n", + "Grant necessary privileges for SelectAI. These commands should be executed as ADMIN user." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sPCUUywDUEVN" + }, + "outputs": [], + "source": [ + "# SQL commands to execute as ADMIN\n", + "# Note: Run these manually as ADMIN user in Oracle SQL Developer or SQL*Plus\n", + "\n", + "admin_sql_commands = \"\"\"\n", + "-- Grant required privileges to DEMOUSER (as ADMIN)\n", + "GRANT execute ON DBMS_CLOUD TO DEMOUSER;\n", + "GRANT execute ON DBMS_CLOUD_AI TO DEMOUSER;\n", + "COMMIT;\n", + "\n", + "-- Configure Network ACL for Cohere API\n", + "BEGIN\n", + " DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(\n", + " host => 'api.cohere.ai',\n", + " ace => xs$ace_type(privilege_list => xs$name_list('http'),\n", + " principal_name => 'DEMOUSER',\n", + " principal_type => xs_acl.ptype_db)\n", + " );\n", + "END;\n", + "/\n", + "COMMIT;\n", + "\"\"\"\n", + "\n", + "print(\"Admin Setup Commands (Execute these as ADMIN user):\")\n", + "print(\"=\" * 60)\n", + "print(admin_sql_commands)\n", + "print(\"=\" * 60)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e09D-HgwUEVN" + }, + "source": [ + "## Section 5: Create Sample Table and Setup AI Credentials\n", + "\n", + "Create a copy of the UNESCO_SITES table and configure Cohere credentials for SelectAI." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PB1uFwpxUEVN", + "outputId": "315afd87-cda5-4226-ce6d-b3d3ed9cb9e9" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "✓ Successfully connected to Oracle Database as DEMOUSER\n", + "✓ Sample table UNESCO_SITES_COPY created for DEMOUSER\n", + "ℹ Credential COHERE_CRED already exists, skipping creation\n" + ] + } + ], + "source": [ + "# Configuration variables\n", + "cohere_api_key = 'YourCohereKey' # Get from https://cohere.ai/\n", + "username = 'DEMOUSER'\n", + "\n", + "# Close admin connection and reconnect as DEMOUSER\n", + "if conn:\n", + " conn.close()\n", + "\n", + "try:\n", + " # Connect as DEMOUSER\n", + " conn = oracledb.connect(user=username,\n", + " password=password,\n", + " dsn=tns_name,\n", + " config_dir=wall_config_dir,\n", + " wallet_location=wall_config_dir,\n", + " wallet_password=wall_pwd)\n", + "\n", + " print(\"✓ Successfully connected to Oracle Database as DEMOUSER\")\n", + " cursor = conn.cursor()\n", + "\n", + "except Exception as e:\n", + " print(f\"✗ Connection failed: {e}\")\n", + " conn = None\n", + "\n", + "if conn:\n", + " # Step 1: Create a copy of the UNESCO_SITES table\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " CREATE TABLE {username}.UNESCO_SITES_COPY AS\n", + " SELECT * FROM UNESCO_SITES\n", + " \"\"\")\n", + " conn.commit()\n", + " print(f\"✓ Sample table UNESCO_SITES_COPY created for {username}\")\n", + " except oracledb.DatabaseError as e:\n", + " if 'already exists' in str(e):\n", + " print(f\"ℹ Table UNESCO_SITES_COPY already exists, skipping creation\")\n", + " else:\n", + " print(f\"⚠ Error creating table: {e}\")\n", + "\n", + " # Step 2: Create AI Credential for Cohere\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " BEGIN\n", + " DBMS_CLOUD.create_credential(\n", + " 'COHERE_CRED',\n", + " 'COHERE',\n", + " '{cohere_api_key}'\n", + " );\n", + " END;\n", + " \"\"\")\n", + " conn.commit()\n", + " print(\"✓ Cohere credential 'COHERE_CRED' created successfully\")\n", + " except oracledb.DatabaseError as e:\n", + " if 'already exists' in str(e):\n", + " print(\"ℹ Credential COHERE_CRED already exists, skipping creation\")\n", + " else:\n", + " print(f\"⚠ Error creating credential: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1JY8028XUEVN" + }, + "source": [ + "## Section 6: Create AI Profile\n", + "\n", + "Create an AI profile that defines which tables SelectAI should have access to." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JT443erhUEVN", + "outputId": "fe065f61-cfed-4da5-a8a9-8a9d59579c4d" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "ℹ Profile COHERE_UNESCO already exists, skipping creation\n", + "✓ Profile COHERE_UNESCO set as active\n" + ] + } + ], + "source": [ + "# Create AI Profile for Cohere\n", + "profile_json = json.dumps({\n", + " \"provider\": \"COHERE\",\n", + " \"credential_name\": \"COHERE_CRED\",\n", + " \"object_list\": [{\"owner\": username, \"name\": \"UNESCO_SITES_COPY\"}]\n", + "})\n", + "\n", + "if conn:\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " BEGIN\n", + " DBMS_CLOUD_AI.create_profile(\n", + " 'COHERE_UNESCO',\n", + " '{profile_json}'\n", + " );\n", + " END;\n", + " \"\"\")\n", + " conn.commit()\n", + " print(\"✓ AI Profile 'COHERE_UNESCO' created successfully\")\n", + " except oracledb.DatabaseError as e:\n", + " if 'already exists' in str(e):\n", + " print(\"ℹ Profile COHERE_UNESCO already exists, skipping creation\")\n", + " else:\n", + " print(f\"⚠ Error creating profile: {e}\")\n", + "\n", + " # Set the profile as active\n", + " try:\n", + " cursor.execute(\"BEGIN DBMS_CLOUD_AI.set_profile('COHERE_UNESCO'); END;\")\n", + " conn.commit()\n", + " print(\"✓ Profile COHERE_UNESCO set as active\")\n", + " except oracledb.DatabaseError as e:\n", + " print(f\"⚠ Error setting profile: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NlLfKQibUEVN" + }, + "source": [ + "## Section 7: Explore UNESCO SITES Data Structure\n", + "\n", + "Let's examine the structure and sample data of the UNESCO_SITES table." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MOb0i1ZaUEVO", + "outputId": "ae1d5f22-15f0-483d-da90-1964bd846241" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "UNESCO_SITES_COPY Table Structure:\n", + "==================================================\n", + "ID | NUMBER | Length: 22\n", + "LATITUDE | NUMBER | Length: 22\n", + "LONGITUDE | NUMBER | Length: 22\n", + "CATEGORY | VARCHAR2 | Length: 50\n", + "STATES_NAME_EN | VARCHAR2 | Length: 255\n", + "REGION_EN | VARCHAR2 | Length: 255\n", + "NAME_EN | VARCHAR2 | Length: 255\n", + "\n", + "Sample Data:\n", + "================================================================================\n", + "ID: 1, Name: Pennsula Valds , Country: Argentina , Region: Latin America and the Caribbean\n", + " Category: Natural, Lat: -42.5, Lon: -64\n", + "--------------------------------------------------------------------------------\n", + "ID: 2, Name: Ischigualasto / Talampaya Natu, Country: Argentina , Region: Latin America and the Caribbean\n", + " Category: Natural, Lat: -30, Lon: -68\n", + "--------------------------------------------------------------------------------\n", + "ID: 3, Name: Jesuit Block and Estancias of , Country: Argentina , Region: Latin America and the Caribbean\n", + " Category: Cultural, Lat: -31.42056, Lon: -64.19111\n", + "--------------------------------------------------------------------------------\n", + "ID: 4, Name: Quebrada de Humahuaca , Country: Argentina , Region: Latin America and the Caribbean\n", + " Category: Cultural, Lat: -23.19986111, Lon: -65.34886111\n", + "--------------------------------------------------------------------------------\n", + "ID: 5, Name: Los Alerces National Park , Country: Argentina , Region: Latin America and the Caribbean\n", + " Category: Natural, Lat: -42.8528, Lon: -71.8728\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "if conn:\n", + " # Get table structure\n", + " cursor.execute(\"\"\"\n", + " SELECT column_name, data_type, data_length\n", + " FROM user_tab_columns\n", + " WHERE table_name = 'UNESCO_SITES_COPY'\n", + " ORDER BY column_id\n", + " \"\"\")\n", + "\n", + " print(\"UNESCO_SITES_COPY Table Structure:\")\n", + " print(\"=\" * 50)\n", + " for row in cursor.fetchall():\n", + " print(f\"{row[0]:20} | {row[1]:15} | Length: {row[2]}\")\n", + "\n", + " # Get sample data\n", + " cursor.execute(\"\"\"\n", + " SELECT ID, NAME_EN, STATES_NAME_EN, REGION_EN, CATEGORY, LATITUDE, LONGITUDE\n", + " FROM UNESCO_SITES_COPY\n", + " WHERE ROWNUM <= 5\n", + " \"\"\")\n", + "\n", + " print(\"\\nSample Data:\")\n", + " print(\"=\" * 80)\n", + " for row in cursor.fetchall():\n", + " print(f\"ID: {row[0]}, Name: {row[1][:30]:30}, Country: {row[2]:20}, Region: {row[3]:20}\")\n", + " print(f\" Category: {row[4]}, Lat: {row[5]}, Lon: {row[6]}\")\n", + " print(\"-\" * 80)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "X9D8BvNvUEVO" + }, + "source": [ + "## Section 8: Geographic Query Examples with SelectAI\n", + "\n", + "Now let's demonstrate SelectAI with various geographic queries on UNESCO World Heritage Sites." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MZRpb9AkUEVO", + "outputId": "41de4f91-1ad5-4c82-c961-af4a91999f3d" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "================================================================================\n", + "GEOGRAPHIC QUERY EXAMPLES WITH SELECTAI\n", + "================================================================================\n", + "\n", + "Query 1: Show me all UNESCO sites in Australia\n", + "======================================================================\n", + "Generated SQL:\n", + "SELECT \"ID\", \"NAME_EN\", \"REGION_EN\", \"STATES_NAME_EN\", \"CATEGORY\", \"LONGITUDE\", \"LATITUDE\"\n", + "FROM \"DEMOUSER\".\"UNESCO_SITES_COPY\" us\n", + "WHERE UPPER(\"STATES_NAME_EN\") = UPPER('Australia')\n", + "\n", + "Results: 20 sites found\n", + " ID NAME_EN \\\n", + "0 7 Heard and McDonald Islands \n", + "1 8 Shark Bay, Western Australia \n", + "2 9 Macquarie Island \n", + "3 10 Fraser Island \n", + "4 11 Australian Fossil Mammal Sites (Riversleigh / ... \n", + "\n", + " REGION_EN STATES_NAME_EN CATEGORY LONGITUDE LATITUDE \n", + "0 Asia and the Pacific Australia Natural 73.500000 -53.100000 \n", + "1 Asia and the Pacific Australia Natural 113.436111 -25.486111 \n", + "2 Asia and the Pacific Australia Natural 158.895556 -54.594722 \n", + "3 Asia and the Pacific Australia Natural 153.133333 -25.216667 \n", + "4 Asia and the Pacific Australia Natural 138.716667 -19.083333 \n" + ] + } + ], + "source": [ + "if conn:\n", + " print(\"\\n\" + \"=\"*80)\n", + " print(\"GEOGRAPHIC QUERY EXAMPLES WITH SELECTAI\")\n", + " print(\"=\"*80)\n", + "\n", + " # Example 1: Region-based query\n", + " prompt1 = 'Show me all UNESCO sites in Australia'\n", + "\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt1}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'showsql'\n", + " ) as generated_query\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " generated_sql = result[0]\n", + "\n", + " print(f\"\\nQuery 1: {prompt1}\")\n", + " print(\"=\" * 70)\n", + " print(\"Generated SQL:\")\n", + " print(generated_sql)\n", + " print()\n", + "\n", + " # Execute the query and show results\n", + " cursor.execute(str(generated_sql))\n", + " rows = cursor.fetchall()\n", + " col_names = [description[0] for description in cursor.description]\n", + "\n", + " df = pd.DataFrame(rows, columns=col_names)\n", + " print(f\"Results: {len(df)} sites found\")\n", + " print(df.head())\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error in Query 1: {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dtYGlcQmUEVO", + "outputId": "d79a0a62-3b24-46fc-be75-c0c6e293be87" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Query 2: Find all cultural heritage sites in Belgium\n", + "======================================================================\n", + "Generated SQL:\n", + "SELECT \"US\".\"ID\", \"US\".\"NAME_EN\", \"US\".\"REGION_EN\", \"US\".\"STATES_NAME_EN\", \"US\".\"CATEGORY\"\n", + "FROM \"DEMOUSER\".\"UNESCO_SITES_COPY\" \"US\"\n", + "WHERE UPPER(\"US\".\"STATES_NAME_EN\") = 'BELGIUM'\n", + "AND UPPER(\"US\".\"CATEGORY\") = 'CULTURAL'\n", + "\n", + "Results: 10 sites found\n", + " ID NAME_EN \\\n", + "0 50 Flemish Béguinages \n", + "1 51 The Four Lifts on the Canal du Centre and thei... \n", + "2 52 La Grand-Place, Brussels \n", + "3 53 Historic Centre of Brugge \n", + "4 54 Major Town Houses of the Architect Victor Hort... \n", + "\n", + " REGION_EN STATES_NAME_EN CATEGORY \n", + "0 Europe and North America Belgium Cultural \n", + "1 Europe and North America Belgium Cultural \n", + "2 Europe and North America Belgium Cultural \n", + "3 Europe and North America Belgium Cultural \n", + "4 Europe and North America Belgium Cultural \n" + ] + } + ], + "source": [ + "if conn:\n", + " # Example 2: Country-specific query\n", + " prompt2 = 'Find all cultural heritage sites in Belgium'\n", + "\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt2}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'showsql'\n", + " ) as generated_query\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " generated_sql_2 = result[0]\n", + "\n", + " print(f\"\\nQuery 2: {prompt2}\")\n", + " print(\"=\" * 70)\n", + " print(\"Generated SQL:\")\n", + " print(generated_sql_2)\n", + " print()\n", + "\n", + " # Execute the query and show results\n", + " cursor.execute(str(generated_sql_2))\n", + " rows = cursor.fetchall()\n", + " col_names = [description[0] for description in cursor.description]\n", + "\n", + " df = pd.DataFrame(rows, columns=col_names)\n", + " print(f\"Results: {len(df)} sites found\")\n", + " print(df.head())\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error in Query 2: {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ea-M4mTfUEVO", + "outputId": "1c8d0798-2b79-44f4-8c2d-3f99bc0314f5" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Query 3: List natural sites with latitude between 40 and 50\n", + "======================================================================\n", + "Generated SQL:\n", + "SELECT \"ID\", \"NAME_EN\", \"REGION_EN\", \"STATES_NAME_EN\", \"CATEGORY\", \"LONGITUDE\", \"LATITUDE\"\n", + "FROM \"DEMOUSER\".\"UNESCO_SITES_COPY\" us\n", + "WHERE UPPER(\"CATEGORY\") = 'NATURAL'\n", + "AND \"LATITUDE\" BETWEEN 40 AND 50\n", + "\n", + "Results: 28 sites found\n", + " ID NAME_EN \\\n", + "0 95 Srebarna Nature Reserve \n", + "1 96 Pirin National Park \n", + "2 103 Miguasha National Park \n", + "3 106 Joggins Fossil Cliffs \n", + "4 300 Gulf of Porto: Calanche of Piana, Gulf of Giro... \n", + "\n", + " REGION_EN STATES_NAME_EN CATEGORY LONGITUDE LATITUDE \n", + "0 Europe and North America Bulgaria Natural 27.078060 44.114440 \n", + "1 Europe and North America Bulgaria Natural 23.430472 41.742722 \n", + "2 Europe and North America Canada Natural -66.353056 48.105000 \n", + "3 Europe and North America Canada Natural -64.435833 45.709722 \n", + "4 Europe and North America France Natural 8.628833 42.325194 \n" + ] + } + ], + "source": [ + "if conn:\n", + " # Example 3: Coordinate-based query\n", + " prompt3 = 'List natural sites with latitude between 40 and 50'\n", + "\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt3}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'showsql'\n", + " ) as generated_query\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " generated_sql_3 = result[0]\n", + "\n", + " print(f\"\\nQuery 3: {prompt3}\")\n", + " print(\"=\" * 70)\n", + " print(\"Generated SQL:\")\n", + " print(generated_sql_3)\n", + " print()\n", + "\n", + " # Execute the query and show results\n", + " cursor.execute(str(generated_sql_3))\n", + " rows = cursor.fetchall()\n", + " col_names = [description[0] for description in cursor.description]\n", + "\n", + " df = pd.DataFrame(rows, columns=col_names)\n", + " print(f\"Results: {len(df)} sites found\")\n", + " print(df.head())\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error in Query 3: {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hR8EyLZUUEVO", + "outputId": "00cc5fd9-de3c-490a-887d-aa04d8f03501" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Query 4: Which countries have the most UNESCO sites and show me the top 5\n", + "======================================================================\n", + "Generated SQL:\n", + "SELECT \"STATES_NAME_EN\" AS country, COUNT(\"ID\") AS site_count\n", + "FROM \"DEMOUSER\".\"UNESCO_SITES_COPY\"\n", + "GROUP BY \"STATES_NAME_EN\"\n", + "ORDER BY site_count DESC\n", + "FETCH FIRST 5 ROWS ONLY\n", + "\n", + "Results: 5 countries found\n", + " COUNTRY SITE_COUNT\n", + "0 China 54\n", + "1 Italy 49\n", + "2 Spain 44\n", + "3 France 41\n", + "4 Germany 39\n" + ] + } + ], + "source": [ + "if conn:\n", + " # Example 4: Complex multi-criteria query\n", + " prompt4 = 'Which countries have the most UNESCO sites and show me the top 5'\n", + "\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt4}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'showsql'\n", + " ) as generated_query\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " generated_sql_4 = result[0]\n", + "\n", + " print(f\"\\nQuery 4: {prompt4}\")\n", + " print(\"=\" * 70)\n", + " print(\"Generated SQL:\")\n", + " print(generated_sql_4)\n", + " print()\n", + "\n", + " # Execute the query and show results\n", + " cursor.execute(str(generated_sql_4))\n", + " rows = cursor.fetchall()\n", + " col_names = [description[0] for description in cursor.description]\n", + "\n", + " df = pd.DataFrame(rows, columns=col_names)\n", + " print(f\"Results: {len(df)} countries found\")\n", + " print(df.head())\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error in Query 4: {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1WRWdnDGUEVO", + "outputId": "7923f368-0087-4b8b-f04a-80fb118d074b" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Query 5: Show me mixed heritage sites in Asia with their coordinates\n", + "======================================================================\n", + "Generated SQL:\n", + "SELECT \n", + " us.NAME_EN AS \"Site Name\",\n", + " us.LONGITUDE AS \"Longitude\",\n", + " us.LATITUDE AS \"Latitude\"\n", + "FROM \n", + " \"DEMOUSER\".\"UNESCO_SITES_COPY\" us\n", + "WHERE \n", + " UPPER(us.REGION_EN) = 'ASIA' \n", + " AND UPPER(us.CATEGORY) = 'MIXED'\n", + "\n", + "Results: 0 sites found\n", + "Empty DataFrame\n", + "Columns: [Site Name, Longitude, Latitude]\n", + "Index: []\n" + ] + } + ], + "source": [ + "if conn:\n", + " # Example 5: Mixed heritage sites in Asia\n", + " prompt5 = 'Show me mixed heritage sites in Asia with their coordinates'\n", + "\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt5}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'showsql'\n", + " ) as generated_query\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " generated_sql_5 = result[0]\n", + "\n", + " print(f\"\\nQuery 5: {prompt5}\")\n", + " print(\"=\" * 70)\n", + " print(\"Generated SQL:\")\n", + " print(generated_sql_5)\n", + " print()\n", + "\n", + " # Execute the query and show results\n", + " cursor.execute(str(generated_sql_5))\n", + " rows = cursor.fetchall()\n", + " col_names = [description[0] for description in cursor.description]\n", + "\n", + " df = pd.DataFrame(rows, columns=col_names)\n", + " print(f\"Results: {len(df)} sites found\")\n", + " print(df.head())\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error in Query 5: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gKZ1yrGaUEVP" + }, + "source": [ + "## Section 9: Generate Natural Language Explanations\n", + "\n", + "Get narrative explanations of what the queries do." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7CpTMyfyUEVP", + "outputId": "6ee2a6c4-7533-433c-febc-e0238d70f61d" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "================================================================================\n", + "NATURAL LANGUAGE EXPLANATIONS\n", + "================================================================================\n", + "\n", + "Explanation for: Show me all UNESCO sites in Australia\n", + "----------------------------------------------------------------------\n", + "Here are the UNESCO sites in Australia:\n", + "\n", + "1. Heard and McDonald Islands\n", + "2. Shark Bay, Western Australia\n", + "3. Macquarie Island\n", + "4. Fraser Island\n", + "5. Australian Fossil Mammal Sites (Riversleigh / Naracoorte)\n", + "6. Greater Blue Mountains Area\n", + "7. Kakadu National Park\n", + "8. Great Barrier Reef\n", + "9. Sydney Opera House\n", + "10. Willandra Lakes Region\n", + "11. Tasmanian Wilderness\n", + "12. Lord Howe Island Group\n", + "13. Gondwana Rainforests of Australia\n", + "14. Uluṟu-Kata Tjuṯa National Park\n", + "15. Wet Tropics of Queensland\n", + "16. Purnululu National Park\n", + "17. Royal Exhibition Building and Carlton Gardens\n", + "18. Australian Convict Sites\n", + "19. Ningaloo Coast\n", + "20. Budj Bim Cultural Landscape\n", + "\n", + "\n", + "Explanation for: Find all cultural heritage sites in Belgium\n", + "----------------------------------------------------------------------\n", + "Here are the cultural heritage sites in Belgium:\n", + "\n", + "1. Flemish Béguinages\n", + "2. The Four Lifts on the Canal du Centre and their Environs, La Louvière and Le Roeulx (Hainaut)\n", + "3. La Grand-Place, Brussels\n", + "4. Historic Centre of Brugge\n", + "5. Major Town Houses of the Architect Victor Horta (Brussels)\n", + "6. Neolithic Flint Mines at Spiennes (Mons)\n", + "7. Notre-Dame Cathedral in Tournai\n", + "8. Plantin-Moretus House-Workshops-Museum Complex\n", + "9. Stoclet House\n", + "10. Major Mining Sites of Wallonia\n", + "\n" + ] + } + ], + "source": [ + "if conn:\n", + " print(\"\\n\" + \"=\"*80)\n", + " print(\"NATURAL LANGUAGE EXPLANATIONS\")\n", + " print(\"=\"*80)\n", + "\n", + " # Get natural language explanation for the first query\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt1}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'narrate'\n", + " ) as explanation\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " explanation = result[0]\n", + "\n", + " print(f\"\\nExplanation for: {prompt1}\")\n", + " print(\"-\" * 70)\n", + " print(explanation)\n", + " print()\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error generating explanation: {e}\")\n", + "\n", + " # Get explanation for the country-specific query\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt2}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'narrate'\n", + " ) as explanation\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " explanation2 = result[0]\n", + "\n", + " print(f\"\\nExplanation for: {prompt2}\")\n", + " print(\"-\" * 70)\n", + " print(explanation2)\n", + " print()\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error generating explanation: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MJTlloY7UEVP" + }, + "source": [ + "## Section 10: Geographic Data Visualization\n", + "\n", + "Create interactive maps to visualize the UNESCO sites data." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "Bd-EL_B9UEVP", + "outputId": "253c96b7-ab8f-4931-e10a-c4cfdba9822b" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "================================================================================\n", + "GEOGRAPHIC DATA VISUALIZATION\n", + "================================================================================\n", + "Loaded 100 UNESCO sites for mapping\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "
Make this Notebook Trusted to load map: File -> Trust Notebook
" + ] + }, + "metadata": {} + } + ], + "source": [ + "if conn:\n", + " print(\"\\n\" + \"=\"*80)\n", + " print(\"GEOGRAPHIC DATA VISUALIZATION\")\n", + " print(\"=\"*80)\n", + "\n", + " # Get all UNESCO sites with coordinates for mapping\n", + " try:\n", + " cursor.execute(\"\"\"\n", + " SELECT NAME_EN, STATES_NAME_EN, REGION_EN, CATEGORY, LATITUDE, LONGITUDE\n", + " FROM UNESCO_SITES_COPY\n", + " WHERE LATITUDE IS NOT NULL AND LONGITUDE IS NOT NULL\n", + " AND ROWNUM <= 100 -- Limit for performance\n", + " \"\"\")\n", + "\n", + " rows = cursor.fetchall()\n", + " col_names = [description[0] for description in cursor.description]\n", + "\n", + " df_sites = pd.DataFrame(rows, columns=col_names)\n", + " print(f\"Loaded {len(df_sites)} UNESCO sites for mapping\")\n", + "\n", + " # Create a world map\n", + " world_map = folium.Map(location=[20, 0], zoom_start=2, tiles='OpenStreetMap')\n", + "\n", + " # Add markers for each site\n", + " category_colors = {\n", + " 'Cultural': 'blue',\n", + " 'Natural': 'green',\n", + " 'Mixed': 'orange'\n", + " }\n", + "\n", + " for idx, row in df_sites.iterrows():\n", + " category = row['CATEGORY']\n", + " color = category_colors.get(category, 'red')\n", + "\n", + " popup_text = f\"\"\"\n", + "{row['NAME_EN']}
\n", + "Country: {row['STATES_NAME_EN']}
\n", + "Region: {row['REGION_EN']}
\n", + "Category: {row['CATEGORY']}
\n", + "Coordinates: {row['LATITUDE']}, {row['LONGITUDE']}\n", + "\"\"\"\n", + "\n", + " folium.CircleMarker(\n", + " location=[row['LATITUDE'], row['LONGITUDE']],\n", + " radius=5,\n", + " popup=folium.Popup(popup_text, max_width=300),\n", + " color='black',\n", + " fillColor=color,\n", + " fillOpacity=0.7\n", + " ).add_to(world_map)\n", + "\n", + " # Add layer control\n", + " folium.LayerControl().add_to(world_map)\n", + "\n", + " # Display the map\n", + " display(world_map)\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error creating map: {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 732 + }, + "id": "YzPciCCJUEVP", + "outputId": "eaa27f45-e90d-485c-a8a6-aa57bbd94401" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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ueXh4SEXFxflypVLX375pa5evRrrtty6dUtffvmlMmbMKCcnJ2XKlEldu3ZVUFDQC8drWr9+verWrau0adNa3hN16tTRzp07X+r5NL9X3dzclDx58pdaNqYaM2fOrFatWkmSfv311yjPWUxjdi1atEhVqlSRl5eXHB0d9e6776pZs2Y6fvx4jI+5f/9+NWzYUOnTp5ejo6M8PDyUNWtW1atXT8uWLXup+s1u3bqljh07RnkdunXrpjt37kRpN3PmTJlMJlWuXDnWdV29elUODg5ycXHRrVu34lXP05ydnZUjRw5Jiva5Yhaf98K///6revXqKXXq1HJ1dVX+/Pn1008/KTIy0jLG1bNjAT5vTKnw8HBNnjxZpUqVkqenp6XuL7/8UleuXImxhqfHpvvzzz/1wQcfyMPDQ8mTJ1fp0qW1atWqODxDAID4IJQCACQqX331ldKkSaN//vkn1qAjoZh7oyxatCjWg5WYmA848+fPr8KFC7/UY965c0dbtmyRJLVo0eKF7Zs3by5JCgkJifOgvpkyZVKWLFkkRQ+czPfNPaWk6D2qzG2yZMkS5ZTDadOmydfXVwsXLpSPj4/q16+vHDly6K+//lKlSpU0ZMiQWGvasWOHihYtqj179uijjz5S9erV5e7u/sJtWbhwoT788EP9/fffypAhgz755BO5uLiobdu26tGjxwuXf122bNmifPny6ccff9SjR4/08ccfq3Tp0jp37pw6d+6s6tWr6/HjxzEu+7LPRaFCheTn52e57+fnF+VmHlspMDBQzZs318qVK/XOO++oSpUqKl++vO7du6dx48apUKFCOnv2bLT1X7t2TSVKlNC4ceN0//591ahRQ++//75+++03lSxZUsHBwbHW9tVXX6lixYpatmyZMmbMqNq1aytr1qxatmyZPvzwQ82cOTOuT6llfwwKCkqQ8Z/q16+v0qVLS3oy1trTz1mVKlUs7cLDw9WwYUM1aNBAmzZtUs6cOVW7dm15eXlpzpw5Klq0qP7+++8o616/fr18fX31xx9/KHXq1KpVq5YqVqwoLy8vrVy58qW22+zOnTsqUaKE5s6dqyJFiqh69eq6e/eufvrpJ/n6+ka5cEGTJk3k5eWldevW6fTp0zGub8qUKQoPD1fjxo2VKlWql64nJiEhIZIUpeenWXzeC5s3b1bx4sW1ePFipUiRQrVq1VLatGnVu3dvNWnS5KXrCw0NVdWqVdW+fXsdPHhQpUuXVu3atRUaGmrZB57XC3fw4MFq0KCBJKlatWrKkSOHduzYoRo1amjJkiUvXQ8AIA4MAABegb+/vyHJkGT4+/s/t22mTJkMScbMmTOjTPfz8zMkGUOHDjUMwzDGjh1rSDLef/99IzIy0tJu6NChhiTDz88v3jU8bc+ePYa9vb0hyXBxcTHq169v/PTTT8aWLVuM+/fvx7rchx9+aEgyWrduHefHMlu/fr2l1gsXLsRpmSxZshiSjEGDBsX5cVq3bm1IMtq1axdletasWQ0vLy/L8+rj42NkyZIlSpsvvvgi2vYdOXLEsLe3N0wmk/Hbb79Fab9q1SrD0dHRkGSsXbs2yjzzayvJ6NOnjxERERGt1o0bNxqSjDJlykSZfu3aNcPd3d2QZIwZMybKvH/++cdwdna2rPtllClTxpBkDB48+LntzLU/+367du2akSpVKsNkMhkTJ06Msk03b940ypcvb0gyhgwZEuP64vNcGIbxwm0NCQkxli1bZoSGhkaZHhYWZvTt29eQZFSrVi3acnXq1DEkGWXLljWCg4Mt0+/cuWN88MEHlsd9dr+dOnWqIcnInj27cfjw4SjzNm/ebLi7uxuOjo7G6dOnY635aREREUbhwoUtj1esWDGjf//+xpIlS4xLly49d1nzc/tsjTNnzozxNXxav379DElGiRIljPPnz0eZt3DhQiNZsmTGO++8Y9y5c8cyvVy5coYkY/bs2dHWFxQUZOzcufOF2/tsjZKMkiVLGrdu3bLMu3PnjlGqVClDktGoUaMoy/Xv39+QZHz55ZfR1hkWFmb4+PgYkoz9+/fHuZbYPqMNwzCOHz9uJEuWzJBk7N27N8q8+LwXHjx4YLz77ruGJKNHjx5R9odjx44Z3t7esX6um/fhjRs3Rpneu3dvQ5KRLVu2KMuEhYUZbdq0MSQZWbJkibaPmB8nRYoUxq5du6LMGzx4sCHJyJkzZ2xPGwDgFRBKAQBeyesIpcLCwoysWbMakox58+ZZ2sUllHrerWDBgtFqWrFihZE+ffpobR0cHIyPP/44WshiGIaRO3duS7DwsubPn295jEePHsVpmZIlSxqSjPbt28f5cX7//fdoB1IXLlwwJBn16tWzTGvYsKEhyfjvv/8s03LlymVIMn7//XfLNPMBXd26dWN8vE6dOhmSjI8//jjKdPNrmzNnTiM8PDzGZWMLYr799lvLgXpMunTp8kqhVFxvz77fzAe+nTp1inH9ly9fNhwcHKKEf4bxas+FYbw4lHqRdOnSGXZ2dkZISIhl2n///WeYTCbDzs7OOHHiRLRljh49aphMpmj7bUREhJEuXTpDkrFv374YH2/EiBGWwCGurl69alStWjXG1yFnzpzG8OHDjQcPHkRbLr6h1K1btwwXFxfD2dnZuHz5coxtOnToYEgyxo0bZ5mWN29eQ5Jx+/btOG9bbJ4OpQ4ePBht/pEjRyyv0dPh3JUrVwwHBwfD09PTuHfvXpRl5s2bZ0gyfH19X6qWmD6jg4KCjDVr1lg+9wYMGBBlmfi+F3777TdDkpEpUyYjLCws2jLjx49/qVDq4cOHhpubmyHJWL58ebT13b9/3xJ0zZkzJ8o88+OMHTs22nKPHj0yPD09DUnGxYsXY9w+AED8cfoeACDRcXBw0LfffitJGjBgQKynQcWkXr160U5vMt8++eSTaO1r1Kih8+fPa8WKFerSpYtKlSolV1dXy5X3KlWqpMGDByfYtsWHYRgvvYx5LKjTp0/r2rVrkqKeumf27LhSAQEBOnXqVJR1PD3/6StyPa1NmzaSpK1btyoiIiLa/Nq1a0e7YtqLmB+zadOmMc5/+pS2+ChYsGCs7xU/Pz9ly5YtxuVWrlwpSWrYsGGM8999913lyJFDN27c0JkzZ6LNj89z8TIOHz6sMWPGqHPnzmrdurVatmypli1bKjw8XJGRkVFO4du6dasMw9D777+v3LlzR1tXvnz5YhzE/+DBg7p69aqyZcumIkWKxFiHedymHTt2xLn2tGnTatWqVfr333/1/fffq2bNmnr33XclPXkv9+nTR76+vgoKCorzOp9n48aNevjwoUqXLm15nGfFtB3FixeX9OS9uW3bthjHEHtZBQsWVKFChaJNN58mHBkZaTn1V5LSpUun+vXrKzg4WL///nuUZSZMmCBJ6tSpU7xqMV98wWQyKUWKFKpcubLOnDmj2bNnR7voQnzfC5s3b5b05EITDg4O0ZaJbb+Pzb59+3Tv3j2lTJlSNWvWjDbf1dXVckGLmC4CISnG5ZycnJQ1a1ZJeqnTvAEAcWNv6wIAAG+2pwdgflF4Yp7/7JXmYtKoUSONHDlSBw8e1JQpU+J8cDVq1Chlzpw5Tm3NHBwcVKNGDdWoUUPSk3FJNm3apAEDBmjfvn365ptvVL16dcuBqJeXl06ePKnr16+/1ONIUurUqS3/DwwMjNNV1syP4+XlFefHMQcjZ86c0caNG9WkSZMog5ybPR1K+fn5WdrkyJEjykG6+WDMPFbVs8wBzqNHj3Tr1q1og8e/7GsiSZcvX37uY8Y2Pa5q16793MHWW7ZsqXPnzkWbfv78eUnShx9++MLHuHHjhnLmzBllWnyei7i4f/++mjdv/sKxb8zjAkn/e46fV1PmzJl1+PDhKNPMz8G5c+deuD8/PRZSXL333nt67733LPdPnDihiRMnasKECTp8+LD69+9vCV5ehXk71q9f/1LbMWzYMB05ckSrV6/W6tWr5eLiovfff19ly5ZV06ZNlSdPnpeu5Xnv5yxZsujAgQOW18vsyy+/1Lx58zRhwgR98cUXkqQjR45o27Zt8vb2Vv369V+6DkkqXbq0smfPLunJdm/dulV3795V+/btlSNHDstnoRT/98KL3nspUqSQp6fnc8c0e9qLPqOk/31OxRYuxfZ5bL7q4KNHj+JUCwAg7gilAACv5OmrZN2/f/+5be/duydJloGZn8dkMmn48OGqXLmyhg4dGmsPndfByclJlStXVunSpZU7d25duXJFy5YtsxyIFSlSRFu3btXevXtfet2FCxeWyWSSYRjavXv3C0OpGzduyN/f3/K4L6NcuXLRQqlUqVIpX758ljZ58+aVl5eXpeeA+d9nr7r3qlxcXBJ0fbYUGRkp6clA2i+6SlxMA0y/rueib9++WrJkiXLnzq3hw4erWLFiSp06tRwdHSVJpUqV0s6dO2MMj58XJsQ0z/wc+Pj4PPcKcFLUIDa+8uTJo3HjxsnOzk5jx47V0qVLEySUMm9H9uzZLYOix+bpnmQ+Pj7at2+fNm/erH/++Ufbt2/X7t27tX37dn3//fcaNmzYa7mC6LOvXcmSJVW8eHHt2bNHmzdvVpkyZSzPy+eff2557V9W27Zto3zmBgcHq06dOtq4caM+/fRTHT9+XK6urpJe/b3wsu+918nOjpNIAMDaCKUAAK8kZcqUcnNz071793T27NkogcfTbt++rdu3b0uK/dfoZ1WqVEkVKlTQ+vXrNXr06Nd6ylNM3Nzc5Ovrq0WLFunmzZuW6bVq1dJPP/2ko0eP6uDBgy91Bb6UKVPqww8/1JYtW/Tbb79ZrvQUG/NpOe7u7jFexv55ypUrp6lTp2rjxo26ePGi/P39VadOnWgHeh999JH+/PNP/ffff5aeUs+GUu+++67OnTun8+fPx/gam3tLODs7K2XKlC9VZ2zeffddnTx5Mtrl4M1im/66ZciQQWfOnFHv3r1VtGhRm9QQkz/++EOStGDBghhPuYvpVEJzb7jnPZcxzcuQIYOkJ6FbQlwpL64qVaqksWPHRtkfX4V5O3LlyvXS22EymVS2bFnLfvno0SPNmjVLHTt2VL9+/VS/fv1YTwGNiTl8jon5NUifPn20eV9++aWaNWum8ePHq2DBgpozZ47s7e0tPacSgqenpxYsWKDcuXPrwoULGjNmjAYMGCAp/u+FF733goODX+o0TfP6nvc8mj+nYjtVEwBgffwcAAB4JXZ2dpZTwP78889Y2y1atEiS9M4778Q4bkpsfvjhB5lMJo0ePTpepwE9T1zGarp48aKkqAeDZcuWtfSqaN++vUJDQ5+7jnPnzlnGdZKkfv36SZL++usvLVu2LNblLl26ZBlbq1OnTpZTSOLKfLB87tw5zZ49O8q0p5lfvzlz5lguL/9sO/P92A46Z8yYIenJKW329gnzm9fTdcXkt99+S5DHeVlVq1aV9L8QyFrM4+7ENn6ROfTNlClTtHlr1qyJMcj58MMPZTKZtH//fstr/7Tjx49HO3VPkqUX1vHjx3Xs2LGX2o7YxHd/fB5zT6HYnrMKFSrI0dFRmzZtitfpuE9zdnbWF198oQIFCigyMlJHjhx5qeWPHDkS4zLHjh3TgQMHZGdnp48++ija/E8//VRp06bV0qVL9d133+n+/fuqU6eO0qVLF+9tiYmXl5cliBo1apQlMIrve8G8LQsXLozx9Zk7d+5L1Ve0aFG5ubnp9u3bWr58ebT5Dx8+1Pz58yUlfE9QAED8EUoBAF5Zr169ZDKZNGfOHP3yyy/R5u/cudMSxPTo0SPGQW1jU6RIETVo0EB3797V9OnTE6xm6cng3AMGDIgy8LPZw4cP9fXXX2vPnj2yt7ePNjbL7NmzlTp1au3evVvly5fX0aNHo63j/v37GjNmjIoUKaLAwEDL9MqVK6tz586SpMaNG2vWrFnRDsh3796tcuXK6c6dOypatGi8Blv38fGxjG0zevRoSc8PpcaMGSPpyalSPj4+Udp06dJF9vb2Wrp0qSXgMlu7dq2mTJkiSfrqq69eus7YtGnTRm5ubtq5c6fGjh0bZd6mTZs0efLkBHusl9GzZ0+lSJFCY8aM0ejRoxUWFhatjb+/f7Tn6VWZg5jYDvzNr/W4ceOiTD916lSsvWYyZ86smjVrKjIyUu3bt9fdu3ct84KDg9W+ffsYwyIHBwcNHjxYhmGoTp062rZtW7Q2ERER2rBhg3bt2hWn7VuxYoVq166tdevWxThY/qZNmyxjgJkHrH4R83N2/PjxGOd7e3urc+fOun//vmrWrBnjfhwaGqrly5fr5MmTlmmjRo2yBGRPO3nypKVHWkzh4PMYhqH27dvrzp07lmlPvwb16tWz9Ep6moODg9q3b6/w8HCNGjVKUvwHOH+RDh06KGPGjAoODrZ8psT3vdCgQQOlTZtW//33n/r37285DVB68jx+8803L1Wbs7OzOnbsKOnJ35kLFy5Y5j1+/FhdunRRQECAsmTJEu+xtgAAr4ENrvgHAEiCfv75ZyNZsmSGJCNr1qxG/fr1jUaNGhlFixa1XFK+UaNGRnh4eLRlzZdzHzp0aIzrPnPmjOHg4GC5bPezl3f39/e3zKtXr57h5+cX623//v2W5WrVqmVZLmvWrEbNmjWNJk2aGBUrVjTeeecdQ5KRLFkyY+LEibHWVaBAAcs68ubNa9StW9do1KiR8eGHHxpOTk6GJMPb29u4cOFClGUjIyON3r17G3Z2doYk49133zVq165tNGzYMMo6K1eubAQFBb3kq/E/5svZSzJSpkxpREZGRmsTGRlppEyZ0tKuQ4cOMa5rypQplnrff/99o0mTJkbp0qUtr+/XX38dbRnza/v0JeaftXHjRkOSUaZMmWjz5s2bZ3lf5c+f32jcuLHx0UcfGSaTyejWrZul5pdhvpz84MGDn9vOXPuz7zfDMIzNmzcbqVOnNiQZadKkMcqXL280bdrUqFGjhpEtWzZDklGiRIkY1xff5+Krr74yJBmpU6c2Pv30U6NNmzZGmzZtjJs3bxqGYRh//vmn5bXInz+/0ahRI6N8+fKGg4ODUb58eaNUqVKGJGPjxo1R1nvlyhUjc+bMhiQjVapURt26dY06deoYKVOmNHLkyGF88sknhiRjzpw50Wrq2bOn5TV47733jFq1ahmNGjUyypYta6RIkcKQZEyaNOm5z7PZkiVLLOvy9PQ0ypUrZzRu3Nj45JNPjNy5c1vmVaxY0bh//36cntvQ0FAjXbp0hiSjcOHCRosWLYw2bdoYI0aMsLR5/Pix0aRJE0OSYWdnZxQuXNioV6+e0bBhQ6N06dJG8uTJDUnG6tWrLct4enoakozcuXMbderUMZo0aWKULVvWsLe3NyQZLVq0iNM2G4ZhzJw505BkfPLJJ0bWrFmNFClSGHXq1DHq1q1r2S9z5MhhBAYGxrqOwMBAy+dNgQIF4vzYz8qUKdML36MzZswwJBnu7u7GrVu3LNPj815Yv3694ezsbEgysmfPbjRq1MioVKmS4ejoaDRo0MDImDGjIcm4cuVKlOXM+/Cz7+VHjx4ZFSpUMCQZLi4uRrVq1YyGDRta1pMqVSpj37590bbpRZ8jsT0eAODVEUoBABLMwYMHjTZt2hg5cuQwXF1dDUdHR0vYsmzZsliXe1EoZRhRw5XnhVIvui1ZssSy3OXLl42ZM2cazZo1MwoWLGikSZPGsLe3N9zd3Y0CBQoYnTp1Mv7999/nbnNERISxYMEC49NPPzUyZcpkuLi4GE5OTkb69OmNGjVqGNOmTYt2AP20f//91+jYsaORO3duw83NzXBycjIyZMhgNGzY0Pjrr7+e+9hxsXDhQsu2165dO9Z2Twd0CxcujLXdrl27jPr16xs+Pj6Gvb29kSpVKqN69erG2rVrY2z/qkGMYRjG1q1bjcqVKxseHh6Gq6urUbhwYWPKlCmGYbz4YDImCRFKGcaTIGDgwIHG+++/b7i7uxuOjo5G+vTpjVKlShmDBw82jhw5EuP64vtcPHz40OjVq5eRPXt2w9HR0bLt/v7+ljZbtmwxKlSoYKROndpwdXU18uXLZ3z33XdGaGjocw+sr1+/bnTs2NFInz694ejoaGTIkMHo2LGjcevWLaN8+fKGJGPNmjUx1rx9+3ajadOmRqZMmQwnJyfD3d3dyJkzp1G7dm1j+vTpxu3bt2Pd3me3b82aNUavXr2M0qVLG5kyZTKcnZ0NZ2dnI2PGjEbt2rWNBQsWxBisPu+5PXr0qPHJJ58YXl5ellA1pud31apVRt26dY13333XcHBwMFKkSGHkyZPHaNSokTF37two+/Hs2bONVq1aGfny5TNSpkxpODk5GZkyZTKqVq1qLFmyJMYaY2MOpfz8/Izr168b7dq1i/I6fPnll1HCn9iUKFHCkGTZN+IjLqFUeHi4kTdvXkOS0adPnyjz4vNeOHz4sCUEdXZ2NvLmzWuMHDnSCA0NNRwdHQ07Ozvj4cOHUZZ53nv58ePHxsSJE42SJUta9sts2bIZnTt3Ni5fvhzjNhFKAYDtmAwjDifwAwAA4K0TFBSkrFmzKjg4WIGBgQlyJT0kvNOnTyt37tzy9PTUlStXLFfGe5Nt2bJFZcqUUf78+V96fC4AwJuDMaUAAADecnv27Ik27caNG/Lz89OdO3dUo0YNAqlEbNCgQZYxqd6kQOrGjRsxXi3v33//1WeffSZJatWqlbXLAgBYET2lAAAA3nImk0np06dXnjx5lCpVKl25ckUHDx7UvXv3lDFjRm3bti3GQbZhO8uXL9eyZct07Ngx7d69Wz4+Pjpx4oRSpEhh69LibNOmTSpXrpzy5s2rrFmzysXFRf7+/jpw4IAiIyP18ccfa9WqVQl2RU8AQOLDJzwAAMBbbsCAAVq/fr0OHz6sO3fuyNHRUdmyZVONGjXUvXt3pUqVytYl4hkHDhzQjBkz5O7urooVK2rMmDFvVCAlSTlz5lTHjh21efNmbd++XXfv3pW7u7tKlSqlJk2a6LPPPiOQAoAkjp5SAAAAAAAAsDrGlAIAAAAAAIDVEUoBAAAAAADA6jhJOwaRkZG6evWq3N3dZTKZbF0OAAAAAADAG8MwDN29e1fp0qWTnV3s/aEIpWJw9epVrjADAAAAAADwCi5duqT06dPHOp9QKgbu7u6Snjx5Hh4eNq4GAAAAAADgzRESEqIMGTJY8pXYEErFwHzKnoeHB6EUAAAAAABAPLxoSCQGOgcAAAAAAIDVEUoBAAAAAADA6gilAAAAAAAAYHWEUgAAAAAAALA6QikAAAAAAABYHaEUAAAAAAAArI5QCgAAAAAAAFZHKAUAAAAAAACrI5QCAAAAAACA1RFKAQAAAAAAwOoIpQAAAAAAAGB1hFIAAAAAAACwOkIpAAAAAAAAWB2hFAAAAAAAAKzO3tYF4PUafvCmrUsAYtWncGpblwAAAAAAsBF6SgEAAAAAAMDqCKUAAAAAAABgdYRSAAAAAAAAsDpCKQAAAAAAAFgdoRQAAAAAAACsjlAKAAAAAAAAVkcoBQAAAAAAAKsjlAIAAAAAAIDVEUoBAAAAAADA6hJVKPX111/LZDJFueXOndsy/9GjR+rYsaNSpUolNzc31atXT4GBgVHWcfHiRVWvXl2urq5KkyaNevbsqfDwcGtvCgAAAAAAAJ7D3tYFPOu9997TP//8Y7lvb/+/Ert166aVK1dq4cKF8vT0VKdOnVS3bl1t375dkhQREaHq1avLx8dHO3bs0LVr19SiRQs5ODjo+++/t/q2AAAAAAAAIGaJLpSyt7eXj49PtOnBwcH65ZdfNHfuXJUvX16SNHPmTOXJk0e7du1SyZIltXbtWh0/flz//POPvL29VahQIQ0dOlS9e/fW119/LUdHR2tvDgAAAAAAAGKQqE7fk6QzZ84oXbp0ypo1q5o2baqLFy9Kkvbv36/Hjx+rYsWKlra5c+dWxowZtXPnTknSzp07lT9/fnl7e1vaVK5cWSEhITp27FisjxkaGqqQkJAoNwAAAAAAALw+iSqUKlGihGbNmqW///5bkyZNkr+/vz788EPdvXtXAQEBcnR0VIoUKaIs4+3trYCAAElSQEBAlEDKPN88LzbDhg2Tp6en5ZYhQ4aE3TAAAAAAAABEkahO36tatarl/wUKFFCJEiWUKVMm/fHHH3JxcXltj9u3b191797dcj8kJIRgCgAAAAAA4DVKVD2lnpUiRQrlzJlTZ8+elY+Pj8LCwhQUFBSlTWBgoGUMKh8fn2hX4zPfj2mcKjMnJyd5eHhEuQEAAAAAAOD1SdSh1L1793Tu3DmlTZtWRYoUkYODg9avX2+Zf+rUKV28eFG+vr6SJF9fXx09elTXr1+3tFm3bp08PDyUN29eq9cPAAAAAACAmCWq0/e++uor1axZU5kyZdLVq1c1ePBgJUuWTI0bN5anp6fatGmj7t27K2XKlPLw8FDnzp3l6+urkiVLSpIqVaqkvHnzqnnz5hoxYoQCAgI0YMAAdezYUU5OTjbeOgAAAAAAAJglqlDq8uXLaty4sW7duiUvLy998MEH2rVrl7y8vCRJP/74o+zs7FSvXj2FhoaqcuXKmjhxomX5ZMmS6a+//lL79u3l6+ur5MmTy8/PT998842tNgkAAAAAAAAxMBmGYdi6iMQmJCREnp6eCg4OfuPHlxp+8KatSwBi1adwaluXAAAAAABIYHHNVRL1mFIAAAAAAABImgilAAAAAAAAYHWEUgAAAAAAALA6QikAAAAAAABYHaEUAAAAAAAArI5QCgAAAAAAAFZHKAUAAAAAAACrI5QCAAAAAACA1RFKAQAAAAAAwOoIpQAAAAAAAGB1hFIAAAAAAACwOkIpAAAAAAAAWB2hFAAAAAAAAKyOUAoAAAAAAABWRygFAAAAAAAAqyOUAgAAAAAAgNURSgEAAAAAAMDqCKUAAAAAAABgdYRSAAAAAAAAsDpCKQAAAAAAAFgdoRQAAAAAAACsjlAKAAAAAAAAVkcoBQAAAAAAAKsjlAIAAAAAAIDVEUoBAAAAAADA6gilAAAAAAAAYHWEUgAAAAAAALA6QikAAAAAAABYHaEUAAAAAAAArI5QCgAAAAAAAFZHKAUAAAAAAACrI5QCAAAAAACA1RFKAQAAAAAAwOoIpQAAAAAAAGB1hFIAAAAAAACwOkIpAAAAAAAAWB2hFAAAAAAAAKyOUAoAAAAAAABWRygFAAAAAAAAqyOUAgAAAAAAgNURSgEAAAAAAMDqCKUAAAAAAABgdYRSAAAAAAAAsDpCKQAAAAAAAFgdoRQAAAAAAACsjlAKAAAAAAAAVkcoBQAAAAAAAKsjlAIAAAAAAIDVEUoBAAAAAADA6gilAAAAAAAAYHWEUgAAAAAAALC6RBtKDR8+XCaTSV27drVMe/TokTp27KhUqVLJzc1N9erVU2BgYJTlLl68qOrVq8vV1VVp0qRRz549FR4ebuXqAQAAAAAA8DyJMpTau3evpkyZogIFCkSZ3q1bN61YsUILFy7U5s2bdfXqVdWtW9cyPyIiQtWrV1dYWJh27NihX3/9VbNmzdKgQYOsvQkAAAAAAAB4jkQXSt27d09NmzbVtGnT9M4771imBwcH65dfftGYMWNUvnx5FSlSRDNnztSOHTu0a9cuSdLatWt1/PhxzZ49W4UKFVLVqlU1dOhQTZgwQWFhYbbaJAAAAAAAADwj0YVSHTt2VPXq1VWxYsUo0/fv36/Hjx9HmZ47d25lzJhRO3fulCTt3LlT+fPnl7e3t6VN5cqVFRISomPHjsX6mKGhoQoJCYlyAwAAAAAAwOtjb+sCnjZ//nwdOHBAe/fujTYvICBAjo6OSpEiRZTp3t7eCggIsLR5OpAyzzfPi82wYcM0ZMiQV6weAAAAAAAAcZVoekpdunRJXbp00Zw5c+Ts7GzVx+7bt6+Cg4Mtt0uXLln18QEAAAAAAN42iSaU2r9/v65fv673339f9vb2sre31+bNmzV27FjZ29vL29tbYWFhCgoKirJcYGCgfHx8JEk+Pj7RrsZnvm9uExMnJyd5eHhEuQEAAAAAAOD1STShVIUKFXT06FEdOnTIcitatKiaNm1q+b+Dg4PWr19vWebUqVO6ePGifH19JUm+vr46evSorl+/bmmzbt06eXh4KG/evFbfJgAAAAAAAMQs0Ywp5e7urnz58kWZljx5cqVKlcoyvU2bNurevbtSpkwpDw8Pde7cWb6+vipZsqQkqVKlSsqbN6+aN2+uESNGKCAgQAMGDFDHjh3l5ORk9W0CAAAAAABAzBJNKBUXP/74o+zs7FSvXj2FhoaqcuXKmjhxomV+smTJ9Ndff6l9+/by9fVV8uTJ5efnp2+++caGVQMAAAAAAOBZJsMwDFsXkdiEhITI09NTwcHBb/z4UsMP3rR1CUCs+hRObesSAAAAAAAJLK65SqIZUwoAAAAAAABvD0IpAAAAAAAAWB2hFAAAAAAAAKyOUAoAAAAAAABWl6ChVFhYmO7fv5+QqwQAAAAAAEASFK9Qav78+erWrVuUaUOGDJGbm5tSpEihOnXq6N69ewlSIAAAAAAAAJKeeIVSo0ePjtIjaseOHRoyZIgqV66sbt266e+//9Z3332XYEUCAAAAAAAgabGPz0Lnzp2Tn5+f5f7cuXPl4+OjJUuWyN7eXpGRkfrzzz81bNiwBCsUAAAAAAAASUe8ekqFhobK2dnZcn/t2rWqWrWq7O2fZFx58+bV5cuXE6ZCAAAAAAAAJDnxCqWyZMmif/75R5K0b98+nT17VlWqVLHMDwwMlJubW8JUCAAAAAAAgCQnXqfvtWvXTl26dNHx48d1+fJlpU+fXjVq1LDM3759u957770EKxIAAAAAAABJS7xCqc6dO8vZ2VmrVq1SkSJF1Lt3b7m4uEiSbt++rYCAAH3xxRcJWigAAAAAAACSDpNhGIati0hsQkJC5OnpqeDgYHl4eNi6nFcy/OBNW5cAxKpP4dS2LgEAAAAAkMDimqvEq6eUWWhoqA4cOKDr16+rdOnSSp2aA0wAAAAAAAC8WLwGOpeksWPHKm3atCpdurTq1q2rI0eOSJJu3ryp1KlTa8aMGQlWJAAAAAAAAJKWeIVSM2fOVNeuXVWlShXNmDFDT58BmDp1apUvX17z589PsCIBAAAAAACQtMQrlBo9erRq1aqluXPnqmbNmtHmFylSRMeOHXvl4gAAAAAAAJA0xSuUOnv2rKpWrRrr/JQpU+rWrVvxLgoAAAAAAABJW7xCqRQpUujmzdiv6nb8+HH5+PjEuygAAAAAAAAkbfEKpapVq6apU6cqKCgo2rxjx45p2rRp+uSTT161NgAAAAAAACRR8Qqlvv32W0VERChfvnwaMGCATCaTfv31VzVr1kxFixZVmjRpNGjQoISuFQAAAAAAAElEvEKpdOnSaf/+/apSpYoWLFggwzD0+++/a8WKFWrcuLF27dql1KlTJ3StAAAAAAAASCLs47tgmjRpNH36dE2fPl03btxQZGSkvLy8ZGcXr5wLAAAAAAAAb5F4JUitW7fW7t27Lfe9vLzk7e1tCaT27Nmj1q1bJ0yFAAAAAAAASHLiFUrNmjVL586di3W+v7+/fv3113gXBQAAAAAAgKTttZxrd/XqVbm4uLyOVQMAAAAAACAJiPOYUsuWLdOyZcss96dOnap//vknWrugoCD9888/KlasWMJUCAAAAAAAgCQnzqHU8ePHtXDhQkmSyWTS7t27tX///ihtTCaTkidPro8++khjxoxJ2EoBAAAAAACQZJgMwzBediE7OzvNnj1bTZo0eR012VxISIg8PT0VHBwsDw8PW5fzSoYfvGnrEoBY9Smc2tYlAAAAAAASWFxzlTj3lHpaZGRkvAsDAAAAAAAAXstA5wAAAAAAAMDzxKmnlJ2dnezs7PTgwQM5OjrKzs5OJpPpucuYTCaFh4cnSJEAAAAAAABIWuIUSg0aNEgmk0n29vZR7gMAAAAAAADxEa+BzpM6BjoHrIOBzgEAAAAg6YlrrsKYUgAAAAAAALC6OIdSAQEB2rJli+7duxdl+uPHjzVo0CBly5ZNrq6uev/997V8+fIELxQAAAAAAABJR5xDqeHDh6tBgwZydHSMMr1Hjx767rvvdOfOHb333ns6deqU6tWrpy1btiR4sQAAAAAAAEga4hxKbd68WTVr1owSSt24cUMTJ05Unjx5dP78ee3du1fHjx+Xl5eXRo8e/VoKBgAAAAAAwJsvzqHUpUuX9N5770WZ9tdffykyMlJfffWVUqRIIUnKlCmTWrVqpd27dydooQAAAAAAAEg64hxKPXr0SG5ublGmbd26VSaTSRUqVIgyPVu2bLpz507CVAgAAAAAAIAkJ86hVJYsWXTo0KEo0zZu3KhMmTIpQ4YMUabfu3dPKVOmTJACAQAAAAAAkPTEOZSqW7eufv31Vy1YsECXLl3Sd999pwsXLujTTz+N1nbXrl3KmjVrghYKAAAAAACApMM+rg179eqlFStWqHHjxjKZTDIMQ7ly5VL//v2jtLt165aWL1+unj17JnixAAAAAAAASBriHEolT55ce/bs0ZIlS3T+/HllypRJtWvXlrOzc5R2V65c0ZAhQ1S/fv0ELxYAAAAAAABJQ5xDKUmyt7dXgwYNntumQIECKlCgwCsVBQAAAAAAgKQtzmNKAQAAAAAAAAmFUAoAAAAAAABWRygFAAAAAAAAqyOUAgAAAAAAgNXFKZQaO3asTp8+/bprAQAAAAAAwFsiTqFUt27dtG/fPsv9ZMmSae7cuQlezKRJk1SgQAF5eHjIw8NDvr6+Wr16tWX+o0eP1LFjR6VKlUpubm6qV6+eAgMDo6zj4sWLql69ulxdXZUmTRr17NlT4eHhCV4rAAAAAAAA4i9OodQ777wTJfwxDOO1FJM+fXoNHz5c+/fv1759+1S+fHnVqlVLx44dk/QkHFuxYoUWLlyozZs36+rVq6pbt65l+YiICFWvXl1hYWHasWOHfv31V82aNUuDBg16LfUCAAAAAAAgfkxGHBKm+vXra926dapdu7Y8PT01fvx4VapUSTlz5ox9xSaTfv7551cuMGXKlBo5cqTq168vLy8vzZ07V/Xr15cknTx5Unny5NHOnTtVsmRJrV69WjVq1NDVq1fl7e0tSZo8ebJ69+6tGzduyNHRMU6PGRISIk9PTwUHB8vDw+OVt8GWhh+8aesSgFj1KZza1iUAAAAAABJYXHMV+7isbOLEieratavWrl2r69evy2Qyae3atVq7dm2sy7xqKBUREaGFCxfq/v378vX11f79+/X48WNVrFjR0iZ37tzKmDGjJZTauXOn8ufPbwmkJKly5cpq3769jh07psKFC8e7HgAAAAAAACScOIVSadKkiTKGlJ2dnWbPnq0mTZokeEFHjx6Vr6+vHj16JDc3Ny1ZskR58+bVoUOH5OjoqBQpUkRp7+3trYCAAElSQEBAlEDKPN88LzahoaEKDQ213A8JCUmgrQEAAAAAAEBM4jSm1LNmzpypUqVKJXQtkqRcuXLp0KFD2r17t9q3by8/Pz8dP378tTyW2bBhw+Tp6Wm5ZciQ4bU+HgAAAAAAwNsuTj2lnuXn52f5//Hjx3XhwgVJUqZMmZQ3b95XKsjR0VHZs2eXJBUpUkR79+7Vzz//rIYNGyosLExBQUFReksFBgbKx8dHkuTj46M9e/ZEWZ95gHZzm5j07dtX3bt3t9wPCQkhmAIAAAAAAHiN4tVTSpKWLVumbNmyKX/+/KpRo4Zq1Kih/PnzK3v27Fq+fHmCFRgZGanQ0FAVKVJEDg4OWr9+vWXeqVOndPHiRfn6+kqSfH19dfToUV2/ft3SZt26dfLw8HhuWObk5CQPD48oNwAAAAAAALw+8eoptWrVKtWrV0+ZMmXS999/rzx58kiSTpw4oalTp6pu3br666+/VKVKlZdab9++fVW1alVlzJhRd+/e1dy5c7Vp0yatWbNGnp6eatOmjbp3766UKVPKw8NDnTt3lq+vr0qWLClJqlSpkvLmzavmzZtrxIgRCggI0IABA9SxY0c5OTnFZ1MBAAAAAADwGpgMwzBediFfX1+FhoZq69atSp48eZR59+/f1wcffCBnZ2ft3Lnzpdbbpk0brV+/XteuXZOnp6cKFCig3r176+OPP5YkPXr0SD169NC8efMUGhqqypUra+LEiVFOzbtw4YLat2+vTZs2KXny5PLz89Pw4cNlbx/3/C2uly58Eww/eNPWJQCx6lM4ta1LAAAAAAAksLjmKvEKpZInT67vv/9eXbp0iXH+zz//rH79+un+/fsvu+pEgVAKsA5CKQAAAABIeuKaq8RrTClnZ2fdvn071vm3b9+Ws7NzfFYNAAAAAACAt0C8Qqny5cvr559/jvH0vN27d2vs2LGqWLHiKxcHAAAAAACApCleA52PGDFCvr6++uCDD1S8eHHlypVL0pOr4e3Zs0dp0qTRDz/8kKCFAgAAAAAAIOmIV0+pLFmy6MiRI/ryyy91584dLViwQAsWLNCdO3fUpUsXHT58WJkzZ07gUgEAAAAAAJBUxGug86SOgc4B62CgcwAAAABIel7rQOcAAAAAAADAqyCUAgAAAAAAgNURSgEAAAAAAMDqCKUAAAAAAABgdYRSAAAAAAAAsLqXDqUePHigIkWKaPLkya+jHgAAAAAAALwFXjqUcnV1lb+/v0wm0+uoBwAAAAAAAG+BeJ2+V6VKFa1ZsyahawEAAAAAAMBbIl6h1MCBA3X69Gk1b95c27Zt05UrV3T79u1oNwAAAAAAACAm9vFZ6L333pMkHT9+XHPnzo21XURERPyqAgAAAAAAQJIWr1Bq0KBBjCkF4K0x/OBNW5cAPFefwqltXQIAAADw0uIVSn399dcJXAYAAAAAAADeJvEaU+pZwcHBnKoHAAAAAACAOIt3KLVv3z5VqVJFrq6uSpUqlTZv3ixJunnzpmrVqqVNmzYlVI0AAAAAAABIYuIVSu3YsUMffPCBzpw5o2bNmikyMtIyL3Xq1AoODtaUKVMSrEgAAAAAAAAkLfEKpfr166c8efLo+PHj+v7776PNL1eunHbv3v3KxQEAAAAAACBpilcotXfvXrVq1UpOTk4xXoXv3XffVUBAwCsXBwAAAAAAgKQpXqGUg4NDlFP2nnXlyhW5ubnFuygAAAAAAAAkbfEKpUqWLKlFixbFOO/+/fuaOXOmypQp80qFAQAAAAAAIOmKVyg1ZMgQ7du3T9WrV9fq1aslSYcPH9b06dNVpEgR3bhxQwMHDkzQQgEAAAAAAJB02MdnoRIlSmjVqlVq3769WrRoIUnq0aOHJClbtmxatWqVChQokHBVAgAAAAAAIEmJVyglSeXLl9epU6d08OBBnT17VpGRkcqWLZuKFCkS4+DnAAAAAAAAgFm8QymzwoULq3DhwglRCwAAAAAAAN4S8Q6lQkNDNW3aNK1atUr//fefJClz5syqVq2a2rZtK2dn54SqEQAAAAAAAElMvAY6v3z5sgoVKqQvv/xShw8flpeXl7y8vHT48GF9+eWXKlSokC5fvpzQtQIAAAAAACCJiFco1bFjR124cEF//PGHrly5os2bN2vz5s26cuWKFixYoIsXL6pjx44JXSsAAAAAAACSiHidvrd+/Xp169ZN9evXjzavQYMGOnDggMaNG/fKxQEAAAAAACBpildPKXd3d6VJkybW+T4+PnJ3d493UQAAAAAAAEja4hVKtWrVSrNmzdKDBw+izbt3755mzpypNm3avHJxAAAAAAAASJridPre4sWLo9wvXLiwVq5cqdy5c8vPz0/Zs2eXJJ05c0a//fabUqZMqQIFCiR8tQAAAAAAAEgSTIZhGC9qZGdnJ5PJJHPTp/8f64pNJkVERCRMlVYWEhIiT09PBQcHy8PDw9blvJLhB2/augQgVn0Kp7Z1CXHCfoTE7k3ZlwAAAPB2iGuuEqeeUhs3bkywwgAAAAAAAIA4hVJlypR53XUAAAAAAADgLRKvgc4BAAAAAACAVxGnnlIx2bZtm2bMmKHz58/rzp070caYMplMOnz48CsXCAAAAAAAgKQnXqHUmDFj1LNnTzk7OytXrlxKmTJlQtcFAAAAAACAJCxeodTIkSNVunRprVixQp6engldEwAAAAAAAJK4eI0p9eDBAzVt2pRACgAAAAAAAPESr1CqXLlyOnr0aELXAgAAAAAAgLdEvEKpcePGaf369Ro1apRu376d0DUBAAAAAAAgiYtXKJUhQwa1a9dOffr0kZeXl5InTy4PD48oN07tAwAAAAAAQGziNdD5oEGD9N133+ndd99V0aJFCaAAAAAAAADwUuIVSk2ePFnVq1fX0qVLZWcXr85WAAAAAAAAeIvFK1EKCwtT9erVCaQAAAAAAAAQL/FKlWrUqKGtW7cmdC0AAAAAAAB4S8QrlBo8eLCOHz+uDh06aP/+/bpx44Zu374d7QYAAAAAAADEJF6hVK5cuXTo0CFNnjxZxYsXl4+Pj7y8vKLdXtawYcNUrFgxubu7K02aNKpdu7ZOnToVpc2jR4/UsWNHpUqVSm5ubqpXr54CAwOjtLl48aKqV68uV1dXpUmTRj179lR4eHh8NhUAAAAAAACvQbyvvmcymRK6Fm3evFkdO3ZUsWLFFB4ern79+qlSpUo6fvy4kidPLknq1q2bVq5cqYULF8rT01OdOnVS3bp1tX37dklSRESEqlevLh8fH+3YsUPXrl1TixYt5ODgoO+//z7BawYAAAAAAMDLMxmGYdi6iNjcuHFDadKk0ebNm/XRRx8pODhYXl5emjt3rurXry9JOnnypPLkyaOdO3eqZMmSWr16tWrUqKGrV6/K29tb0pOrBfbu3Vs3btyQo6PjCx83JCREnp6eCg4OloeHx2vdxtdt+MGbti4BiFWfwqltXUKcsB8hsXtT9iUAAAC8HeKaqyTqy+cFBwdLklKmTClJ2r9/vx4/fqyKFSta2uTOnVsZM2bUzp07JUk7d+5U/vz5LYGUJFWuXFkhISE6duyYFasHAAAAAABAbOJ1+t4333zzwjYmk0kDBw6Mz+olSZGRkeratatKly6tfPnySZICAgLk6OioFClSRGnr7e2tgIAAS5unAynzfPO8mISGhio0NNRyPyQkJN51AwAAAAAA4MXiFUp9/fXXsc4zmUwyDOOVQ6mOHTvq33//1bZt2+K9jrgaNmyYhgwZ8tofBwAAAAAAAE/E6/S9yMjIaLfw8HCdO3dO3bp1U9GiRXX9+vV4F9WpUyf99ddf2rhxo9KnT2+Z7uPjo7CwMAUFBUVpHxgYKB8fH0ubZ6/GZ75vbvOsvn37Kjg42HK7dOlSvGsHAAAAAADAiyXYmFJ2dnbKkiWLRo0apRw5cqhz584vvQ7DMNSpUyctWbJEGzZsUJYsWaLML1KkiBwcHLR+/XrLtFOnTunixYvy9fWVJPn6+uro0aNRQrF169bJw8NDefPmjfFxnZyc5OHhEeUGAAAAAACA1ydep++9yEcffaTevXu/9HIdO3bU3LlztWzZMrm7u1vGgPL09JSLi4s8PT3Vpk0bde/eXSlTppSHh4c6d+4sX19flSxZUpJUqVIl5c2bV82bN9eIESMUEBCgAQMGqGPHjnJyckrQ7QQAAAAAAED8vJZQat++fbKze/lOWJMmTZIklS1bNsr0mTNnqmXLlpKkH3/8UXZ2dqpXr55CQ0NVuXJlTZw40dI2WbJk+uuvv9S+fXv5+voqefLk8vPzi9Pg7AAAAAAAALCOeIVSv/32W4zTg4KCtGXLFi1evFht27Z96fUahvHCNs7OzpowYYImTJgQa5tMmTJp1apVL/34AAAAAAAAsI54hVLmXksxSZ06tfr06aNBgwbFtyYAAAAAAAAkcfEKpfz9/aNNM5lMeuedd+Tu7v7KRQEAAAAAACBpi1colSlTpoSuAwAAAAAAAG+Rlx+NHAAAAAAAAHhFce4pVaBAgZdasclk0uHDh1+6IAAAAAAAACR9cQ6lUqZMKZPJ9MJ2AQEBOnXqVJzaAgAAAAAA4O0U51Bq06ZNz50fEBCgH374QVOmTFGyZMnUvHnzV60NAAAAAAAASVS8Bjp/WmBgoIYPH66pU6fq8ePHatasmfr3769s2bIlRH0AAAAAAABIguIdSpl7Rj0dRg0YMEBZs2ZNyPoAAAAAAACQBL10KBUQEKDhw4dr2rRpevz4sZo3b64BAwYoS5Ysr6M+AAAAAAAAJEFxDqWuXbtmCaPCw8PVokUL9e/fnzAKAAAAAAAALy3OoVS2bNkUGhqqQoUKqV+/fsqSJYvu3LmjO3fuxLrM+++/nyBFAgAAAAAAIGmJcyj16NEjSdLBgwf16aefPretYRgymUyKiIh4teoAAAAAAACQJMU5lJo5c+brrAMAAAAAAABvkTiHUn5+fq+zDgAAAAAAALxF7GxdAAAAAAAAAN4+hFIAAAAAAACwOkIpAAAAAAAAWB2hFAAAAAAAAKyOUAoAAAAAAABWRygFAAAAAAAAqyOUAgAAAAAAgNURSgEAAAAAAMDqCKUAAAAAAABgdYRSAAAAAAAAsDpCKQAAAAAAAFgdoRQAAAAAAACsjlAKAAAAAAAAVkcoBQAAAAAAAKsjlAIAAAAAAIDV2du6AAAA8HYYfvCmrUsAYtWncGpblwAAwFuHnlIAAAAAAACwOkIpAAAAAAAAWB2hFAAAAAAAAKyOUAoAAAAAAABWRygFAAAAAAAAqyOUAgAAAAAAgNURSgEAAAAAAMDqCKUAAAAAAABgdYRSAAAAAAAAsDpCKQAAAAAAAFgdoRQAAAAAAACsjlAKAAAAAAAAVkcoBQAAAAAAAKsjlAIAAAAAAIDVEUoBAAAAAADA6gilAAAAAAAAYHWEUgAAAAAAALA6QikAAAAAAABYHaEUAAAAAAAArI5QCgAAAAAAAFaXqEKpLVu2qGbNmkqXLp1MJpOWLl0aZb5hGBo0aJDSpk0rFxcXVaxYUWfOnInS5vbt22ratKk8PDyUIkUKtWnTRvfu3bPiVgAAAAAAAOBFElUodf/+fRUsWFATJkyIcf6IESM0duxYTZ48Wbt371by5MlVuXJlPXr0yNKmadOmOnbsmNatW6e//vpLW7Zs0eeff26tTQAAAAAAAEAc2Nu6gKdVrVpVVatWjXGeYRj66aefNGDAANWqVUuS9Ntvv8nb21tLly5Vo0aNdOLECf3999/au3evihYtKkkaN26cqlWrplGjRildunRW2xYAAAAAAADELlH1lHoef39/BQQEqGLFipZpnp6eKlGihHbu3ClJ2rlzp1KkSGEJpCSpYsWKsrOz0+7du2Ndd2hoqEJCQqLcAAAAAAAA8Pq8MaFUQECAJMnb2zvKdG9vb8u8gIAApUmTJsp8e3t7pUyZ0tImJsOGDZOnp6flliFDhgSuHgAAAAAAAE97Y0Kp16lv374KDg623C5dumTrkgAAAAAAAJK0NyaU8vHxkSQFBgZGmR4YGGiZ5+Pjo+vXr0eZHx4ertu3b1vaxMTJyUkeHh5RbgAAAAAAAHh9EtVA58+TJUsW+fj4aP369SpUqJAkKSQkRLt371b79u0lSb6+vgoKCtL+/ftVpEgRSdKGDRsUGRmpEiVK2Kp0AAAAIEEMP3jT1iUAsepTOLWtSwDwhklUodS9e/d09uxZy31/f38dOnRIKVOmVMaMGdW1a1d9++23ypEjh7JkyaKBAwcqXbp0ql27tiQpT548qlKlij777DNNnjxZjx8/VqdOndSoUSOuvAcAAAAAAJCIJKpQat++fSpXrpzlfvfu3SVJfn5+mjVrlnr16qX79+/r888/V1BQkD744AP9/fffcnZ2tiwzZ84cderUSRUqVJCdnZ3q1aunsWPHWn1bAAAAAAAAELtEFUqVLVtWhmHEOt9kMumbb77RN998E2ublClTau7cua+jPAAAAAAAACSQN2agcwAAAAAAACQdhFIAAAAAAACwukR1+h4AAAAAAK8TV7FEYvc2XcmSnlIAAAAAAACwOkIpAAAAAAAAWB2hFAAAAAAAAKyOUAoAAAAAAABWRygFAAAAAAAAqyOUAgAAAAAAgNURSgEAAAAAAMDqCKUAAAAAAABgdYRSAAAAAAAAsDpCKQAAAAAAAFgdoRQAAAAAAACsjlAKAAAAAAAAVkcoBQAAAAAAAKsjlAIAAAAAAIDVEUoBAAAAAADA6gilAAAAAAAAYHWEUgAAAAAAALA6QikAAAAAAABYHaEUAAAAAAAArI5QCgAAAAAAAFZHKAUAAAAAAACrI5QCAAAAAACA1RFKAQAAAAAAwOoIpQAAAAAAAGB1hFIAAAAAAACwOkIpAAAAAAAAWB2hFAAAAAAAAKyOUAoAAAAAAABWRygFAAAAAAAAqyOUAgAAAAAAgNURSgEAAAAAAMDqCKUAAAAAAABgdYRSAAAAAAAAsDpCKQAAAAAAAFgdoRQAAAAAAACsjlAKAAAAAAAAVkcoBQAAAAAAAKsjlAIAAAAAAIDVEUoBAAAAAADA6gilAAAAAAAAYHWEUgAAAAAAALA6QikAAAAAAABYHaEUAAAAAAAArI5QCgAAAAAAAFZHKAUAAAAAAACrI5QCAAAAAACA1RFKAQAAAAAAwOoIpQAAAAAAAGB1STaUmjBhgjJnzixnZ2eVKFFCe/bssXVJAAAAAAAA+H9JMpRasGCBunfvrsGDB+vAgQMqWLCgKleurOvXr9u6NAAAAAAAACiJhlJjxozRZ599platWilv3ryaPHmyXF1dNWPGDFuXBgAAAAAAAEn2ti4goYWFhWn//v3q27evZZqdnZ0qVqyonTt3xrhMaGioQkNDLfeDg4MlSSEhIa+3WCt4dO+urUsAYhUS4mjrEuKE/QiJHfsS8OrYj4BXx34EJIw3ZV96HnOeYhjGc9sluVDq5s2bioiIkLe3d5Tp3t7eOnnyZIzLDBs2TEOGDIk2PUOGDK+lRgBPRN/rAMQH+xLw6tiPgFfHfgQkjKS0L929e1eenp6xzk9yoVR89O3bV927d7fcj4yM1O3bt5UqVSqZTCYbVobEJCQkRBkyZNClS5fk4eFh63KANxb7EvDq2I+AV8d+BLw69iPExjAM3b17V+nSpXtuuyQXSqVOnVrJkiVTYGBglOmBgYHy8fGJcRknJyc5OTlFmZYiRYrXVSLecB4eHnzgAgmAfQl4dexHwKtjPwJeHfsRYvK8HlJmSW6gc0dHRxUpUkTr16+3TIuMjNT69evl6+trw8oAAAAAAABgluR6SklS9+7d5efnp6JFi6p48eL66aefdP/+fbVq1crWpQEAAAAAAEBJNJRq2LChbty4oUGDBikgIECFChXS33//HW3wc+BlODk5afDgwdFO9QTwctiXgFfHfgS8OvYj4NWxH+FVmYwXXZ8PAAAAAAAASGBJbkwpAAAAAAAAJH6EUgAAAAAAALA6QikAAAAAAABYHaEUAAAAAAAArI5QCgAAAAAAAFZHKIW32tMXn+RClAAAAADAsRGsh1AKb63IyEiZTKYo9wHEj/mLy8mTJ3Xs2DEbVwO8ecz70MWLFxUQEKDAwEAbVwQAeFsZhiGTyaT169dr9OjRioiIsHVJSMIIpfBWioyMlJ3dk7f/5MmT5efnpwYNGmj27Nk2rgx485i/uCxevFh16tTRggULFBAQYOuygDeKyWTSn3/+qfLly6tkyZJq0aKFli5dauuygDcKPTuAV2f+Xvfnn3+qYcOGOnv2rM6ePWvrspCE2du6AMAWzIFUnz59NHv2bNWqVUvp06dXixYtFBAQoC+//FKOjo42rhJ4M5hMJq1bt07NmjXTmDFj1KBBA6VKlcrWZQFvBPOX/wsXLqhz584aNGiQ7OzstGvXLnXp0kUPHjxQkyZNbF0mkOiZ96WNGzdq48aNOnv2rNq2bat8+fIpTZo0ti4PeGOYTCbt2LFDrVu31s8//6yWLVvauiQkcSaDnxTwlpo3b5769++v+fPnq3jx4lq7dq2qVKkiSerdu7eGDBlCMAXE4M6dO3rnnXckPTkIePz4sdq1aydPT0/99NNPlgODiIgIJUuWzMbVAonfrl27tGHDBt2+fVujRo2SJJ0+fVrjx4/X4sWLNWLECIIpIA6WLFkiPz8/ffLJJwoKCtL58+dVo0YNdejQQZkzZ7Z1ecAbY8KECdqwYYP+/PNPBQUFaevWrfrtt98UEhKiZs2aqVGjRnJwcLB1mUgi6CmFt4b5QFmSQkNDFRISop49e6p48eJauXKlmjZtqmnTpslkMqlt27ZKkSKFunbtKicnJxtXDiQeEyZM0IEDBzR58mTZ29vLZDLJ0dFR/v7+ypcvnyRZ9jNzIHXx4kVlzJjRZjUDiVlwcLDGjx+vZcuWqVKlSpbpOXPmVOfOnSVJ/fv316NHj9S6dWtblQkkenv37lXXrl31008/qXXr1nrw4IFSpUqlRYsW6dGjR/rqq6/4WwQ8x9PHSo8fP9aSJUu0dOlSTZo0Sfb29vL09JSzs7O+/vprVaxYUWnTprVxxUgqGFMKb4WnBzUPDQ2Vk5OTqlevripVqujq1avq37+/Bg0apDZt2qhEiRJyc3NT37599csvv9i4ciDxOHLkiCIjI9W3b185ODgoNDRUkvTgwQMlT55c165dk/S/iwYYhqFr165pwoQJOn36tM3qBhIzT09PtWvXTjVr1tSaNWu0bt06y7wcOXLoyy+/VLly5TR69GiFhITYsFIgcbt27Zrq1q2r1q1by9/fX++9955atWqlzz77TNOnT9ePP/7IuDhADMwnToWFhVn+37VrV/n5+alz585699131adPH82dO1dTpkyRs7Ozbt++bcuSkcRw+h6SvKcHNR85cqROnDihH3/8UZ6enpKk/fv3q0WLFlq4cKHy5s2r8+fP66efflLNmjVVrlw52dvToRD44osvdODAAa1fv17u7u7asWOHxo0bp++++05Zs2bVhg0b9PHHH2vw4MEaNGiQZbl+/frp77//1urVq+Xt7W3DLQASh6d/iX7a7t279eOPP+r48eMaPXq0Pv74Y8u8c+fOyc3NjX0IeIp5XzL/2BgUFKTr168rc+bMqlu3rry9vS0/LubKlUshISHy8/PT0KFDOe0I+H/m/WjNmjWaMmWKgoOD5enpqREjRih79uy6fv16lDHZ+vTpozVr1uiff/5h/FAkGI62keSZA6levXppzpw56tWrl27evGkJpezs7HTixAlt2rRJoaGhGjBggCRZDgjCw8MJpvBW27lzp/7++2/NmTNH7u7uMgxDly9f1okTJ/T1119ryJAhKl++vCZOnKiOHTtq9+7d8vDwkMlk0qpVq7Rx40YOpgH978v/tm3btHr1ahmGody5c6tFixYqUaKEOnXqpIkTJ6p79+766aefVKFCBUlStmzZbFw5kLiY96W1a9dqy5YtatiwofLnz68UKVLoypUr+u+//9SmTRtJ0vXr11W4cGFlz55dn332GYEU8BSTyaTly5ercePG+vLLL1WiRAkNGjRIlStX1po1a5Q9e3ZJ0po1a7RixQrNnTtXGzZsIJBCguL0PSRZ5lOIJGndunWaO3euFixYoC5duli+4BuGocKFC2vYsGHq1KmTPv30UwUGBlouw20YBoEU3noRERG6fv26MmbMqBUrVqh8+fL69NNP1aFDB/n7+2vgwIG6dOmS2rVrp+3bt8vb21thYWFKlSqVdu7cqcKFC9t6E4BEwWQyafHixapZs6ZOnz6tEydOaNiwYerZs6ck6YMPPlCHDh1UuHBh+fn5adOmTbYtGEikzPtSvXr1ZBhGlAvT3Lt3T4Zh6PTp0zp16pQmTZqkixcvqlevXsqUKZMNqwYSn+DgYI0aNUqDBg3SsGHDVLp0ad29e1eVKlWyBFK3b9/WwYMHdfLkSW3ZskWFChWybdFIcjh9D0nOkCFDNHjw4CjTpk6dql9++UXbt2+3hEzPnkJx9OhRhYWFqXDhwrKzs6OHFPD/IiIi1KpVKy1fvlwPHz7UL7/8ombNmkmSpkyZotmzZytjxowaOnSosmbNqrCwMDk6OnL1PeAZe/bsUYMGDdSvXz+1a9dO//77r8qWLavg4GD5+flp+vTpkqSNGzdq7ty56tu3r7JmzWrjqoHE5+TJk6pSpYoGDBigtm3bRpvfs2dP/fHHH5KejJOzcuVKvf/++9YuE0iUnh7a5ObNmypTpozWrVsne3t7FS5cWDVq1NCUKVMkSYsXL1bdunX14MEDhYWFKUWKFDasHEkVPaWQpKxbt07bt29XeHh4lOmGYejGjRu6fv16lOmPHz/WnDlzdPv2beXPn19FihSRnZ2dIiMjCaTwVhs2bJiWLFki6clV9D744AOFhITIwcHBckqRJLVr107NmjXTxYsX9fXXX+v8+fOWX6zNX3gAPHHo0CFVq1ZN7dq108WLF/XJJ5+oZs2aGj16tH777TdLj6ly5cpp7NixBFKAnhwU37hxI8q0W7duycXFRRUqVIhycQ2zkSNHat68eZo5c6Z2795NIIW3mnkfuX//vqQn38+OHj0qSUqdOrXeeecdTZkyRSVLltQnn3yi8ePHS5ICAwM1fvx4LV68WK6urgRSeG04YkCS8uGHH+rvv/+Wvb29Fi9ebJmeJUsWPXz4UH/88Yfu3Lkj6UnX74iICE2ZMkWzZs2Ksh4OpvE2u3r1qq5cuaLcuXNbpl27dk0//vijKlWqpLx58+rUqVOWee3atVOLFi106NAhDR8+3BIKxzSYM/A2a926tfz8/PT48WO1bt1aZcqU0cyZM1W3bl35+Pho9OjR6tChgyTJxcXFxtUCthUZGal///1XLVu2VFhYWJR5//33n/z9/eXj42Pp3W7+m7Nv3z4dP35cpUqVUvny5ZUxY0ZblA8kGnZ2drp06ZI+++wz7dq1S4sXL1bBggW1Z88eGYZhGRc0a9asmjRpkmXctZ9//lm3bt1SsWLFbLwFSOo4fQ9JxtOn2x07dky+vr6qXLmyFi5cKOlJV+7p06erU6dO+vDDD5U8eXINGTJEt27d0u7du+kZBTzl0aNHcnZ21pYtW3T37l1Vr15dknTq1Cn16NFD27dv1+7du5UzZ07LMrNmzVK5cuUYswPQ/04Rf/jwoRwcHPT48WNL0HT69GnVr19fU6dOVcmSJXX9+nV17dpVlSpV0ocffsjA5sBTQkJC5OHhoePHjytVqlTy9vbWjRs39NFHH+mDDz7Q+PHj5eTkZDklqV27dsqUKZN69+7NKeTA/9u3b586duwowzB05MgRTZ8+3TIUg7+/v7p06aKAgACVKVNGOXPm1O7du7Vo0SJt3rxZBQsWtHH1SOroDoIkwxwqzZo1S9euXdOkSZO0b98+NWzYUNKTrtzdu3fX+vXrVb16dXXu3FkRERHatWuX7O3tFRERYcvygUTB3MXb2dlZ9+7d08yZM9WuXTvL2By5cuXSmDFjVLp0aZUoUUJnzpyxLNuyZUsCKUD/C6RWrVql1q1by9fXVz169NDKlSslSU5OTrpy5YrWrFmj0NBQ/fTTT7p48aJq1KhBIAX8v8jISBmGIQ8PDwUHBytfvnzq37+/bty4oVSpUqlVq1Y6cuSIPv/8c924cUNHjx7VgAED9Oeff6pOnToEUsBTihYtqlatWmnfvn3KnTt3lO9rWbJk0ahRo1SlShWtXbtW06dPV3BwsLZt20YgBaugpxTeeE8P1vfjjz9q0KBBOnjwoLy9vbVy5Ur17NlTvr6+loPqgIAA3b59W05OTsqSJQuDmgNPMR9M379/X8mTJ9e///6rcePGacuWLfr6668tIe/p06fVq1cvLV++XKdPn7ZcoQXAE8uXL1fDhg01YMAAeXl5aePGjfrjjz/077//KmvWrBo9erRGjBihVKlSKSQkRGvXruVKlcBTng53c+TIobNnz6p27dpq27atRo4cKZPJpFmzZmnKlCk6efKk5SB7/vz57EvAU8zHSnPmzNGNGze0YsUKubq6qkOHDqpatWq0tubb01e1BF4nQikkGYcOHdKqVauUPXt2ffrpp5KkBw8eaPny5erZs6dKly6t+fPnR1vu6VALeJs9fQDw559/qk2bNipVqpQOHz6s8ePHa9u2bVGCqRMnTmjIkCEaMmSIcuXKZePqgcQjODhYDRo0ULVq1dS1a1fduHFDhQoVUp06dSwDyIaGhuro0aM6d+6cSpYsSS9DIAY7d+5UxYoVNX78eMtVYGvXrq327dvrhx9+kJubmx4/fqwNGzbIx8dH3t7e8vHxsXXZQKLw7JXGzbZv365+/frJ3d1dnTt3VuXKlSVJ//zzjypUqMCYoLA6QikkCXv37lWJEiVkZ2enGTNmqEWLFpZ55mCqT58+ypMnj1avXm3DSoHEbcmSJWrevLl69Oihxo0bWwY7P3LkiMaNG6dt27bpm2++UYMGDSQ9uYKleUBMAE/cvn1bJUqU0Pz585U2bVoVL15c1apV09SpUyVJixYtUqFChehhCDzl+++/V8WKFVW8eHFJTwYznz59upycnDRw4EDLj4jmYKpDhw4aOHCgvL29bVw5kPiYA6lt27Zp27Ztun79uj755BMVL15crq6u2rFjhyWYqlWrlq5evaqvv/5aly9fVrp06WxdPt4ydA/BG+nZLLVYsWKaMmWKTCaT9u/fr9DQUMs8V1dX1apVS4MGDZKzs7NlzBwAUV24cEF9+/bViBEjNGTIkChX3ytQoIA6d+6sjz76SJ07d9aSJUskiUAK+H9P/11KliyZ8ubNq/3796t06dKqVq2aJk2aJEm6fPmyVq1apX///Tfa3zLgbfXDDz9owIABcnd3l/Tk71HdunX166+/ytXVVdKTfSwiIkKffPKJli5dqmnTpqlv3766ceOGLUsHEh1zILV48WJVq1ZNhw4d0rZt2zRw4EANHTpUd+/eValSpTR8+HAlS5ZMY8eO1ezZs7V3714CKdgEoRTeOJGRkZZupeZLBEdGRuqzzz7TqFGjNG7cOMvpEWYuLi5q2rSplixZIjs7O4IpIAYPHz5UeHi4SpUqZTlYfvqguUCBAurUqZM+/fRTFShQwFZlAolGeHh4jPuKp6enMmfOrC+++EKFChXS5MmTLYMuT5gwQbt371aRIkU4RQLQk6vrrVy5UqNHj1aePHm0detWZcyYUXXr1lVoaKjWrFmjwMBAyz4UGRmpTz75RHPmzNGyZcv4Tgc8w2QyaefOnerSpYt+/PFHzZ8/X3/88YcOHjyoxYsXa8CAAbp3755KliypiRMnasWKFdq2bZuKFCli69LxlmJkZ7xRnh7/aezYsdqzZ49u3bqlokWLqlu3burSpYskqVu3bjKZTOrevbtlWScnJ8v/GUMKiO7mzZs6f/68HB0dZTKZolwA4ODBg3r48KFKlSqlUaNGMfgl3mr79u1T0aJFLfvHmjVrNHPmTDk4OChz5swaOnSofv75ZwUFBWnFihUaNmyYkiVLJn9/f82bN09btmxRhgwZbLwVQOLg4eGhXLlyafz48XJyclKnTp20detWDRgwwDI4808//aSuXbvK29tbERERioyMVP369VWlShW5ubnZehOARMH8w73JZNKxY8dUtWpVtWnTRv7+/qpYsaIaNGig1KlT69dff5Wzs7MGDBhAzygkCowphTdSnz59NGPGDPXr108PHjzQ9OnTlSFDBq1bt06Ojo4aN26cevToof79+2vw4MG2Lhd4Izx48EDVq1eXs7Ozpk6dqgwZMli6gH/xxRdycHDQ6NGjCaTwVtuwYYMaNWqkfv36qWvXrtqwYYMqVqyoxo0bKzIyUuvXr1emTJk0e/Zs5cqVS927d9eRI0d0584d5cuXTz179lS+fPlsvRlAomD+G3Px4kV98skn+vfff/X999+rV69eljaDBw/WihUrVKVKFXXp0kXe3t6WHyljG8gZeBuY94MHDx7IxcVFJpNJe/fuVbFixfTgwQNduHBB2bJlU7Vq1ZQhQwbNnDlTjx49Uq5cufT48WM1bdpUI0aMYB+CzdFTCm+cAwcOaMWKFVq6dKlKlSqlFStW6ObNm+rTp4/lYLlz584KCQnR33//rUGDBvFhCzzF/CX+1KlTunv3rh4+fKgPP/xQrq6uat26taZNm6aWLVtq+PDhun//vlavXq0//vhDW7ZsIZDCWy9Dhgxq1qyZpk6davlVesyYMeratask6fr166pQoYL8/Py0a9cujRkzRnfv3pWjo6Ps7OwYhw1vvad7vZu/n12/fl0XLlxQ7ty5NW/ePNWpU0c5cuSQJA0ZMkSStHr1at2/f18DBgyQl5dXlOWBt5GdnZ3+++8/9ejRQwMHDtTp06fVqFEjbdu2TaVKlVKePHl06tQpXblyRV9//bWkJ/taoUKFVLBgQX322WfsQ0gUOIcJb5xbt24pLCxMpUqV0tKlS9WkSRONGDFCn3/+ue7fv68//vhDoaGh6t+/v7Zs2SKTycRgssD/MwdSS5cuVeXKldWiRQtVrlxZfn5+unbtmpo3b67u3bvL2dlZpUuXVrt27bR27Vpt2LCB3h2ApBw5cqhz586qXr26fv31V40ZM0YpUqSQ9ORqlGnSpNG6det07tw5DRs2TJLk7u4uJycnAilATw6kT548qf79++vChQuSpEyZMmnlypWaMWOGvLy8VL9+fZ09e9ayzJAhQ1SmTBkdPnzYVmUDiZJhGDp8+LBat26t5s2ba+bMmSpVqpRlrDXzvzt27NDt27c1Y8YMhYaGqlu3bpxGjkSDnlJI1J7ulm3+Ze2dd95Rzpw5NX36dHXr1k2jRo1Su3btJD3pRbV69WoVKFBAuXPntgRS/AoAPGEymbR27Vq1atVKP/zwg1q1aqUNGzaoatWqevDggUaPHq3atWurdu3aOnz4sFKmTClXV1elSpXK1qUDiUaWLFnUrl07mUwmTZw4UUeOHJH05GqU4eHhSpMmjYoXL65r167ZuFIg8Xn8+LFatGihffv2aeHChapRo4Y+/PBD1alTR5I0dOhQ9evXT3Xr1tXixYuVPXt2SdLIkSN18+ZNpU6d2pblAzZlPh569OiRwsPDlSVLFg0cOFBt2rRR7ty5lSdPHkn/Gz83c+bMKlOmjKZPn67x48crLCxMq1at0jvvvGPLzQCiYEwpJFpPd+9+WlBQkEqWLKnTp09r5MiR6tGjhyTp0aNHqlu3rpInT64FCxYwmDneejHtQ/fv31eXLl2UMWNGDRo0SP7+/vr4449VuHBh/fPPP/rggw80cuRI5c6d20ZVA4mL+YeNmH7gOH/+vMaNG6c5c+aod+/elr9HklS5cmVlzZpVEydOlMRpRsDTRo4cKXt7e+XLl0/bt2/X2LFjVbVqVZUrV05t2rTRzp07NWLECF24cEHz5s3jbxKg/32vO3PmjMaNG6f8+fOradOmWrNmjW7evKnJkycrZcqU6tu3r8qXL29Z7u7duzpw4ICuXr0qX19fZc6c2XYbAcSAUAqJ3k8//aQDBw7Ix8dHdevWVcmSJXXu3DmVLl1aBQoUUK1ateTm5qbff/9dgYGBOnjwoOzt7WMNtYC3gfn9f/78ef3666+6efOm3nvvPXXo0EFLlizRe++9p9SpU1sCqenTp2vBggVq3LixqlSponHjxilbtmy23gwgUQgLC5Ojo6N27dqlEydO6N69e6pXr57SpUuna9euadSoUZo1a5YaNWqkbNmy6fr16/rxxx918OBB5c2b19blA4nOpk2bVKtWLa1fv15FixbVtWvXNHXqVA0bNkzFixdXixYt9PjxY61bt06BgYHatGkTp7/irWb+XnfkyBFVr15dZcqUUcOGDVWzZk1Lm/Pnz6tu3bry8vJS//79VbZsWUlPrhBbuXJlG1UOvBihFBK177//Xj///LPKli2ry5cv6/Lly5o8ebKqVq2qEydOqFOnTgoMDFTKlCmVNWtWTZs2zXL6hPlS3cDbxvzF5fDhw6patapy5cqlK1eu6OzZs+rWrZtGjx4tSZo9e7YmTpyoBQsWKEOGDJo/f76mTp0qf39/bd68WRkzZrTxlgC2M2XKFP3yyy/as2ePJGn+/Plq06aNsmbNquDgYN27d08jR45UixYtdOPGDY0cOVK///67XF1dNXDgQH3wwQeW0ygARNezZ09du3ZN06dPl7Ozsxo1aqTDhw+rWLFiunbtmjZv3qzPPvtM/fv357L1gGT5Ub5Vq1YaOHCgXF1dLfPMvXnPnj2r+vXry9vbWw0bNtSFCxc0dOhQ+fv7K2PGjPTaRaLEUTsSlWd7N4WEhOjPP//UBx98oOPHj2vMmDFq0qSJ5s6dq6pVq2rlypV6+PChHBwc5ObmJkkEUnirGYZh+SWtVKlS6tq1q4YMGaLAwEDNnz9fPXv2VKlSpVSvXj35+/srJCREyZMnlyQdPnxYderU0RdffMEv0nhrGYahyMhIeXl56fbt26patar+/PNPLV26VOPHj1f9+vXl7u6uL7/8Uv369VOyZMnUsmVLde7cWWFhYTpy5Ijq1KnDuDfAC5QoUUJjxoyRo6Oj2rZtq02bNmn9+vV67733dOLECW3YsEFly5YlkMJbzxw4/frrrypbtqzlIhqSdOPGDZ0/f15nzpxRgQIFVKBAAS1evFifffaZJk+erODgYO3fv1+ZMmWy4RYAz0dPKSQaTwdSW7dulZ2dnfr06aPvv/9eH374oSTp7NmzGj58uJYsWaI5c+aoSpUqUdbBoOaAdOfOHRUvXlweHh7av3+/ZfqJEydUtmxZ/fzzz5ZfpEuWLKn3339fLi4u2rdvn7Zs2aICBQrYsHrAtk6cOKE8efIoLCxM//zzj3r06CEPDw+5ublp3LhxUU7H69SpkxYsWKCTJ08qVapU8vf3V/LkyZUmTRobbgHw5ihTpoy2bdsmHx8frVq1SgULFrR1SUCi1a5dOwUFBWnOnDmyt7fXn3/+qcWLF+uvv/5S8uTJFRAQoOXLl6tGjRq6c+eOgoODlTx5cnl5edm6dOC5GHAHiYY5kOrdu7cqV66sNm3aaN++fTp37pylTfbs2dWnTx/Vq1dP1apVs5xWYUYgBTy5slGTJk104cIFjRw50jI9MjJSd+/elaenpySpYMGC2rRpk7Jly6Y8efJo+/btBFJ4qy1atEilS5dWSEiIHB0dVbFiRY0YMUJ2dnbatm2bnJycJD25YIAkjRgxQvb29lq1apWkJ1flI5ACXsz8m3jv3r2VPXt2TZgwQQULFhS/lQOxc3d316FDhzR+/Hh17NhRnTp1kpubm+bMmaN9+/apRYsW+uqrr3Tz5k298847ypw5M4EU3gic4wSbe7p304EDB7R69Wpt2LBBDx480KJFi/TZZ58pderUqlGjhqQnwVT37t2VNWtWvf/++7YsHUiU0qRJo06dOsnJyUnffvutPD091aJFC1WqVEmfffaZqlatKkmKiIhQiRIlVKxYMZlMJkJdvPU+/vhjHTlyRB4eHrp+/brSpEmjihUrSpK6dOmiFi1aaPv27ZZTXu/fv6/kyZNHGdcDwIuZ/94UKVJEkZGR2r9/v2rXrs3fISAG5mOlUaNG6cyZM5o3b56Cg4M1YcIElS5dWt7e3pKkHDly6MSJE/Lw8LBxxcDL4fQ9JBojRozQlStXZGdnpx9//FHSk9OQBg8erEmTJmnp0qWqXr16tOUYQwqI2Y0bNzRt2jT98MMPun//vrp166aRI0fKMAzL2FMAojt9+rRy586tJUuWqFatWnr06JHWr1+vjh07Kl26dJo0aZIeP36s5cuXa+LEidq7d6+yZMli67KBN9Ls2bP1xRdfaMOGDSpevLitywESpaePd4KDg+Xo6CgXF5cobTp37qybN29qxowZ0eYBiRlHJEg0AgMDNW7cOB06dEgPHjyQJL3zzjv65ptv1KFDB9WvX1+LFi2KthyBFBAzLy8vtW3bVv3795e7u7ulC7fJZOIUCeAZ5n3i0KFDun//vj7//HM1adJEq1atkrOzsypWrKgJEyYoMDBQxYsX19ChQ3XhwgWtXbuWQAp4BeXKlVOxYsUY0Bx4Dnt7e0VGRkqSPD09LaeTS1JQUJD69eunuXPnauDAgQRSeOPQUwo29/Tpe8OHD1f//v01ffp0tWrVytImKChIXbp00X///afNmzfbqlTgjRQYGKhffvlFP/zwgwYNGqQePXrYuiQgUVq5cqXatm2ruXPnqkCBAho0aJCmTZumpUuXqlq1apYeU926dVPWrFm1fPlyOTo62rps4I336NEjOTs727oM4I3zzTff6L///tPGjRu1ZMkSFSpUyNYlAS+NLiawuoULF+q///5T27Zt5erqKicnJ0VERChZsmTq06eP7t69q88//1z29vZq3ry5JClFihSaMGEC43YAMXjRVSe9vb312WefKVmyZOrZs6ccHR3VuXNnK1YIJF7m/efOnTtavny5evbsqXLlykmSvvvuO0lS7dq1LcFUuXLlNHbsWOXIkYNACkggBFLAk7E+7ezsYvxOZz5WelZQUJDc3d21du1a5ciRwxplAgmOUApWYxiGrly5ooYNG8rOzk4HDx6Ui4uL+vTpo6xZs1rafffddzIMQ23btpWdnZ2aNm0qSXJzc5P05ApijIUD/O9gOjg4WK6urnr48KE8PT1jDKm8vLzUsmVLOTg4qFKlSjaqGEh8TCaTdu7cqWbNmillypRq0KCBZV6KFCkswVSDBg00Z84c1a5dW1WqVLFVuQCAJObatWtKmzatJXTaunWrtm3bpmTJkqlIkSKqUKGCkiVLFiWYMn/XGzNmDD0N8cbj9D1Y3cCBA5U6dWplyZJFf/75p/7++281atRIvr6+atSokaVdnz59NGLECK1atYoDAOAZ5i8jK1eu1NixYxUUFCRJGjp06HNDJ0JdIGalSpXSrl27NG7cOLVv3z7KfmI+hXz16tXy9/eXq6srVwkDALyyyZMn688//9TQoUNVsmRJrVq1SjVr1lTFihV14sQJeXp6qnDhwvrtt98kRf0e96Ke8sCbgiMTWJ2Hh4fmz5+vmjVr6tdff9XixYvl5OSkJk2aqHXr1ho/frykJ+NL/fLLL5bLcQP4H5PJpL/++kv169fXxx9/rG+//VZ58uRRlSpVdPjw4ViXI5ACojIPHLtjxw6VLl1aw4cP1/bt2y3TpSc9psaOHasjR44oefLkHAQAABJEwYIFdfbsWY0aNUobN27U9OnTNW7cOK1Zs0ZHjhxR165ddeDAAbVs2VJS1O9x/C1CUkFPKdhE6dKl5evrq1GjRkmS8uTJozRp0ihTpkw6cuSITp48qeXLl1t6fDx9GVQAUlhYmBo1aqQiRYqof//+unTpksqVK6cKFSpoypQplnb8igZEZd4nLl++rNDQUHl4eFiuTClJJUqU0K1bt/Trr7/K19eXIBcA8FqYez3t37/f8p3u6tWrGjt2rGXA8rt372revHmaOHGiJk2aJF9fX9sWDbwGfNOCVZl/eW7UqJGCgoIUEhKiAgUKKFWqVFq3bp2mT5+uv/76S71791b58uUtyxFIAVGFhobqyJEj+uijj3Tnzh35+vpGCaSmTZumCxcuEEgBTzEHUsuWLVOFChVUtWpV5cqVS+PGjZO/v78kaffu3UqVKpXatm2rLVu2ROkxBQBAQrGzs1NkZKSKFCmiuXPn6tChQ9q2bZsOHTpkaePu7q7atWvr8uXLUaYDSQmhFKzK/ItzjRo1tGjRIqVOnVre3t5asmSJHB0d5ejoqPTp02vIkCGyt7dXeHi4jSsGEid3d3d99NFHWrx4sQoUKKCaNWtqwoQJkqTg4GD9888/WrNmjegMC/yPyWTS6tWr5efnpy+++EJ79+5Vhw4dNHjwYP3yyy86d+6cpCfBVEREhHr06KHQ0FAbVw0ASKrMx0bFihXTggULlCNHDs2ePVvbtm2ztEmdOrVy5szJdzokWZy+B6szd1UdM2aMZsyYoTlz5qhgwYK2LgtItMz7zMOHD2UYhlxdXSVJP/zwg3744QcVKlRIf/31l2V6v379tGjRIq1du1aZM2e2YeWA7cR06uqNGzfUpk0blShRQv3799fly5dVrlw5pUiRQqdOndIXX3yhdu3aKVu2bJIkf39/ZcmSxRblAwCSKPPfp9u3b+vhw4dKly6dIiMjlSxZMu3Zs0dNmzbVu+++q0aNGum9997TqlWrNHbsWB0+fFjZs2e3dflAgqOnFKzO/IvA+++/r4cPHyogIECSFBERYcuygERl/fr1lh4adnZ2Wr58uWrUqKEKFSrom2++kST17t1b9evX19WrV9W2bVsNGjRIzZo106RJk/THH38QSOGtFRkZKZPJpFu3bmnv3r06ePCgwsLC5OLiooYNG8rPz083btxQ5cqVVbZsWe3du1ft2rXTL7/8ovHjx1t6TBFIAQASkjmQWr58uSpXrixfX1999NFH+v333xUUFKTixYtr7ty5CggIsPTkvXDhgnbt2kUghSSLUAoJKraxN2LqkFe2bFl99NFH+vzzz/Xo0SMlS5bsdZcHvBFOnTqljz/+WL1795Ykbd26Vc2bN1fevHlVokQJ/fDDD2rYsKEePHigqVOn6vPPP1dERIQ2btwoDw8Pbd++3TJAJvC2MfcsPHbsmGrXrq1evXpp8eLFCgsLk5ubm6pWrar06dNr1qxZSps2rYYPHy5J8vHxkZubmzZs2CAPDw8bbwUAICkymUxauXKlmjVrpjp16mjdunXy8fHR8OHDNXHiRN25c0fFihXTH3/8YTltb/LkycqfP7+tSwdeG0aPRoIICwuTo6OjpRfU2rVrdfPmTb3//vvKnDmznJ2do5xKYT5oqFGjhh49eiRHR0dblg8kKrly5dIff/yhFi1ayMXFRQUKFNCAAQPUs2dPSVLTpk1VsWJFtWjRQrNmzVL37t0lPblKZbJkyRjcHG8twzAsgdRHH32kDh06qGXLlpbT8SQpZcqUkp6cypcsWTLL363r169r9OjRqlChgt555x2b1A8ASNquXr2qYcOGadCgQfrqq68UHBysvXv3ysXFRb/++qvs7Oz0xRdfqECBAlq1apVSpEjBDyVI8hhTCq+sd+/ecnFxUZ8+feTs7Kzu3btr7ty5ioiIUOrUqdWoUSN17txZKVOmtIRRZk8fRD87D3jbmD+OzQfWixcvVuPGjeXi4qIePXpo4MCBlrZ79+5VhQoVVKNGDY0ePVpp06a1VdlAonL79m3VqFFD+fPnt1yNUlK0vzETJ05U3759VbduXYWEhGjNmjXat2+fcufObYuyAQBJlPmH+Zs3b8rOzk5LlixR5cqV5eDgoA8//FAVK1bUxIkTValSJZ05c0aNGzdWz549+YEEbw0SALyS8PBw/ffff/r77781ceJEbd68Wfv27dOyZct08uRJ1apVS6tXr9b333+v27dvy87OLsqBt729vUwmk+UgHHgbmU97DQ0Nlclkkp2dnc6dO6fatWtr8eLFioiI0L///qsHDx5IerLvFCtWTBs2bND8+fM1YMAAxmQD/p+/v7+Cg4Pl5+cXZbr5b4x5X+nQoYMGDhyoO3fuKDw8XDt37iSQAgAkOJPJpHnz5qlu3bq6d++eatasqfTp02vcuHHKly+fhg0bJkkqUqSIQkNDdfjwYb7X4a1CCoB4M4dKv//+uwoVKqRly5Zp3rx5ypcvn0qUKKFUqVJp+PDhqlq1qrZu3aphw4bp9u3blhDq6VOMON0IbzM7OztdunRJbdq00eXLl7Vs2TIVLlxYZ86cUfXq1fX7779r6dKlGjhwoMLCwiz7UNGiRbV//3716tWLMdmA/3f8+HEFBAQoZ86ckqKPaZgsWTKFhYVp9+7d+uqrr7Rw4UItWrSI8ToAAAnK/Pfn/v37mjx5surWrauMGTMqTZo0kqSAgAAZhiFnZ2dJT36c/OGHHzRjxgylTp3aZnUD1saYUog3wzBkGIYcHR01fvx4devWTbNnz472S/PgwYMlSWvWrFHv3r01evRozo0GnnHgwAFdunRJ9evX16FDhzRjxgzlypVLkZGRql27tubNm6dGjRpJkoYPHy4HBwcZhqHChQvbuHIgcfH29tadO3d09OhRlStXLsY2Y8eO1ZkzZ1SiRAk5ODhYuUIAwNvAZDJp3bp1mjlzpt599119+umnkv4XVqVKlUqHDx9W3759dffuXc2fP19HjhyRt7e3LcsGrI6eUoiX/fv3y87OTnZ2dvr++++1evVq/fjjj2rRooWuX7+u4cOH6/79+5b2gwcPlq+vr0wmk9zd3W1YOZA41apVSxUrVtSePXuUN29eFStWTJIsvaLq1q2r+fPna/LkyerUqZMeP35MD0MgBjlz5lTBggU1dOhQnTlzRiaTSeHh4VHaXL58WV5eXtGmAwCQkG7fvq2lS5fq77//toRRkZGRMplMGjJkiPLly6cjR47o5MmT2rZtm7JkyWLjigHrY6BzvLQLFy4oS5Ys6tKli5IlS6Zp06Zpx44deu+99xQWFqbOnTvr0KFDatCggTp27CgXFxfLsubT9p49fQ94m0VERChZsmSaMmWKLl26pP3798vBwUFDhgxR4cKFLV9eTCaT5s+fry5duvBLGvAcI0aM0IgRI1S+fHkNHTpUuXLlkiQFBwdrxIgR+vXXX7VhwwbLKX4AAMTXsxfSePp+WFiYVq5cKT8/PzVs2FDTpk2TJD1+/FgODg6KjIxUZGSkQkNDlTx5cpvUD9gaoRRemmEYWrt2rWrVqiV7e3sdOnRI2bNnV2hoqJycnCzB1OHDh/Xpp5/qiy++kKura5TlCaTwNntROLtw4UJNmTJFLi4uGjp0qAoVKiTpSQ/F999/X/fv35ebm5uVqwYSv6cPBPr166eZM2fKzs5OLVu2VGBgoIKCgrR161b9/fffnPoKAEgwJ0+e1O+//67PP/9cGTNmjPL97vHjx1qyZIlatmyptm3bauzYsZKeXDDK3p7RdABO30Ocma8QZjKZdPbsWUVGRio8PFyTJk2SJDk5Oenx48dydHTUuHHjVLhwYf38889atmxZlPUQSOFtNmvWLC1cuFDh4eGWYMrMvI81aNBAX3zxhUJDQzVgwABt2LBBQ4YMUeXKlXX79m0CKSAWdnZ2lisWff/99xo/fryqVaumJUuW6MSJE8qePbu2bt1KIAUASDCPHz9WixYtNGzYMH388cfq1auXFi5caJnv4OCgTz75RDNnztS0adPUrVs3SSKQAv4fewLizPzrc+/evXXhwgXt2bNHly9f1qeffqqwsDCNGzfOMmCsOZjKlSuXZVA/4G33+PFjjRs3Tv/X3n2H13z3fxx/npOcJBKzRmILsdVWNVpbjRBi09smIQTVWDGrZtyxakSpVesmsXdVrVIECXpTW20JIohEcvL7wy+nUtpbW3KC1+O6el3JdyTvo1dycl7n/Xl/ABwcHGjQoAG2traWjimj0Wj5uHnz5tja2vL111/Tvn177O3t2bx5M5kzZ7byoxBJnZK6pGxsbCwfN2vWjGbNmvHgwQPSpk373BILERGRf8pkMtGiRQvatGlDiRIl2LdvH15eXqxbt45KlSrh7e2Ng4MDrVq1AqBNmzbY2dkxYcIEK1cukjpo+Z78JaGhoXTp0oWgoCAqVqxIQkICwcHBdOzYke7duzNlyhQAfHx8aNy4MZ988gnw28wckXdVUtgUHR1Ns2bNuH//PgMHDqRRo0bJgqlnrwX49ddfiYyMJGvWrOTMmdOaD0EkVUj6+Th69Cjnz5/n9u3bNG/e/E+3z04Ko7R8XEREXocffvgBDw8PduzYQfny5bl+/Tpz5sxh4sSJvP/++3Tp0oUaNWrg5ubG6tWrKVq06HM7lou8qxRKyUubOHEiZ86cISEhgblz51rebU5ISCAkJIQOHTrw4YcfEhcXx61bt/j555/VliryjLi4OOzs7Lhx4wYeHh44OjrSt29f3N3dsbGx+cNgSkSSCw4Opk+fPuTOnRuA//73v8ybN4/GjRtbOnZFRERSkp+fH9evX2fu3Lk4ODjQunVrwsLCqFixIhcuXGD//v0EBATg6+urv/FEnqHEQF7avXv3mDdvHkWLFuXmzZtkz54dABsbG1q0aEHevHmZNm0a2bJlY8KECdja2qpDSuT/JSYmYmdnx4oVK9i4cSO2trbs37+fa9euYTAYnlvKpz9WRF7s8OHDeHt7M3HiRDp16sStW7dwcXHh/PnzlkBKoa6IiKS0ihUrEhgYiJ2dHV27duWHH35gx44dFC9enNOnT7N161Zq1aql5yeR31GnlLzQH83dCAwM5PPPP2fcuHH07NmTdOnSAS/eTUw7Sogkt3//fmrXrs2MGTMoX7685V20J0+eMHr0aBo2bPhcx5SIJBcSEsKSJUsIDg7mzJkz1K5dm3r16hEUFAT81pGonyMREUlp1apVY+/evbi4uLBp0yZKlSpl7ZJEUj0lBvKcZwOpCxcuEBMTg4uLC++99x6fffYZUVFRDBkyBEdHRzp16kTatGmfC6QSExMVSIn8Tnh4OAUKFKBly5Y4OjoCsHv3bqpWrYqfnx8A9evX1/IjkT9x9uxZbty4wc2bN6lTpw716tWz7AK7YsUK9u7dy+TJk/UcJCIiKSbpddDAgQO5ceMGEyZMoFSpUnqDROQlaAsaSSYxMdESSPn7+9OkSRPKlSuHh4cHvXv3BmDUqFH4+/vTr18/FixYQHR0NECyX7j65Svym6SG1Li4OGJjY7G3twfg0aNHODo6Mn/+fC5fvsyIESPYunWrNUsVSTXMZvMLjzdu3BiDwYCbmxu1atWydEgBHDp0iCtXrvDo0aOUKlNERMTy2qdcuXKYzWZCQ0OTHReRP6ZQSpJJ+sU5YcIEZs+ezZgxY1i/fj21a9fm+++/p2XLlgB88cUXjBgxAl9fX72IFnmBZ1dGJ/1cNWzYkF9//ZURI0YAWLqlYmJiqFq1Ks7OzpQoUSLlixVJRWJjYwEsb5AcP36czZs3c+TIEQDy5MlDhQoVcHZ2plChQsDTXSr9/f1ZsGABY8aMIX369NYpXkRE3mnOzs6MGDGCyZMnc/DgQWuXI/JGUG+7WCQNJY+Ojmb37t0MHz4cd3d3ACpVqkShQoUYPXo0U6ZMoW/fvgwbNozcuXPTpEkT6xYuksoktWofOnSI0NBQXF1dKVq0KPnz52fKlCn06dOHhIQEBg8eTEJCAps2bcLV1ZWpU6eSJk0aa5cvYjVjx47lwYMH+Pn5kSlTJlavXk27du3IkycPv/zyCwMGDGDw4MEMHz6c6OhovvnmG8aOHUuhQoWIiopi+/btFCtWzNoPQ0RE3mE1atSgQoUK5MiRw9qliLwRFEoJ27dvp3DhwuTJkwcABwcHrl27xrlz5yzXODk54enpycqVKy3vVgN07NgR0FBzkWcZDAZWr15Nhw4dyJkzJw8ePKBixYqMGDGC7t27Y2NjQ79+/Vi+fDkmk4nIyEi2b9+uQEreefb29gwdOhQnJydatGjBpEmTmDZtGh4eHmzfvh0vLy/u37/P+PHj+eqrr7h69Sq7du2iaNGi5M2bVy8ARETE6nLmzMnmzZtxcHCwdikibwSlCO+4W7duMWrUKCIjI9m2bRu5c+fGbDZTpUoVzp49y+nTpylcuDDw9MVCiRIlOHDggGV3oyQKpER+c/36ddauXcuUKVNo3749K1euZNGiRfTp04cpU6bQpUsX6tSpw65du7C1taVixYrkz5/f2mWLWF3//v1xdHTEx8eHx48fU6hQIVq1akW6dOlo27Yt9vb2dOrUCYPBwKBBgyhQoAAFChSwdtkiIiLJKJASeXmGxGcHn8g7adu2bQQGBnL79m1CQkLImzcvBw8exN3dnQYNGtCnTx/KlCnDgwcPcHd3p0iRIsyePdvaZYukSqGhoYwZM4aHDx8yZ84c8ubNC8CGDRuYMWMGsbGxTJw4kfLly1u5UhHre3a318ePH1v+iJ87dy7du3cna9as/Pjjj8mCp+DgYLy8vGjUqBGjRo2ydPmKiIiIyJtHg86FunXr0r9/fzJlykTTpk25cOECH3zwAStXruSHH36gW7dulCpVijp16hAZGcn06dOB5IOcReSp0NBQzpw5Q2hoqGVgM4C7uzs+Pj6kTZuW7t27ExYWZsUqRVIHo9HIlStXuHHjBg4ODqxZs4Z58+bRtWtX5s+fz+3bt1m4cCH37t2z3NOsWTOmTZvG999/b9nJUkRERETeTOqUegcdOHCAhw8fUqlSJcvuXwA7d+5k1KhRREVFERwcTP78+fn55585duwY4eHh5MyZkx49emBra6sZUiJ/4ttvv2XixIm4urry73//Gzc3N8u5kJAQli9fTkBAgKWLSuRdFR0dTdu2bYmKiuJf//oXXl5eLFmyhDZt2gAwc+ZMevXqxRdffEHv3r3JkCFDsnvTpUtnrdJFRERE5BVQKPWO+e6776hbty4A+fLlo0GDBpQpU4bGjRuTNWtW9u/fz9ixY7l8+TJr164lX758z32NpF36RN51SbvsRUVFYTQaMRqNODk5AU+XHy1evJjs2bMzduzYZDOjHjx4QNq0aa1VtkiqkZiYyKZNm/j88885d+4ckyZNwtfXl9jYWEsXVFIwNWbMGLy9vcmUKZPlXoPBYM3yRUREROQf0vK9d4yTkxMlSpSgUqVKlChRAoCBAwfy8ccfU6tWLU6cOEGlSpXIli0bn376KVeuXHnuayiQEvntBfGGDRto1aoVpUuXxtfXl+XLlwPQtWtX2rVrx/Xr1xk+fDhnzpyx3KtASuS3n6GiRYsSGxtL7ty52bJlC9evX8fe3p64uDgAevbsyYwZM/D392fevHmWpeMKpERERETefOqUegft2bMHf39/8uTJg7e3N6VLl2b//v1888033Lx5kz179pApUyYiIiLw9vZm5syZ1i5ZJFVav349rVq1wt/fH1dXVzZt2sSPP/7I4MGD6datG/C0Y2r69OmUL1+e2bNnYzKZrFy1iHU9O9wcICoqilu3bnH69GkCAgKws7Nj4cKF5MiRI9lOr0uWLKFMmTIUK1bMWqWLiIiIyCumUOod9f333zNs2DAyZ87MgAEDqFq1KvD0xcGJEyfYv38/v/zyCzNnztTsKJEXOHv2LC1btsTLywsvLy+ioqIoUqQI2bJl4+HDhwwaNIiuXbsCsHDhQqpXr64ZUvJOO3XqFEWKFAFevPQuMTGRVatW8dVXX+Hg4MCCBQvInj07gYGBZMyYkc6dO1ujbBERERF5jRRKvcN27drFkCFDyJYtGz179qROnTovvE5DzUWeFxkZyejRoxk4cCAJCQnUrFmT2rVr07t3bzp37szly5cZOHAgvr6+1i5VxOqWL19OYGAgfn5+tGjRAkgeTD378apVq5g5cya//vor1apV45tvviEsLIz333/favWLiIiIyOuhmVJvKS8vLw4dOvSn11SrVo0xY8Zw69YtZs+ezc6dO194nQIpkeQSExPJnDkzX375JdmzZ2fSpEmUKVOG8ePHU7RoUUqXLo3JZGLFihXcuXMHZf/yritRogTp06dn3rx5rFy5Eng6E+rZ+VBJHzdv3pwRI0ZQv3597t69S3h4uAIpERERkbeUOqXeQrGxsVSqVIk7d+6wZs0aSpcu/afX79q1i2HDhmEwGJg8eTJly5ZNmUJF3hK1atXCzc2NoKAgAHx8fChYsCDt27fnvffes3J1ItaV1G174cIFfHx8MBqNdOrUiWbNmgF/3DEFJJspJSIiIiJvH3VKvYXs7e3Zu3cvhQsXplGjRhw7duxPr69WrRpDhw6lWLFi/zPAEpHfJCYmYjabKVeuHGFhYUyaNInPPvuM4OBgmjZtqkBKBCxDzW1tbalSpQrHjh0jICCAdevWAc93TD1LGwOIiIiIvN3UKfWWSUhIwMbGBoArV67g7u6OjY0N8+bNe+nA6fc7I4m8y5I6N140mDnJTz/9xOzZs9m3bx/p06fn66+/pkyZMilcqUjqFRISQocOHejWrRt3795l48aNFClShD59+rywY0pERERE3g0Kpd5SgwYN4vjx40RGRnLkyBFy585NcHCwOqFEXlLSC+TfLx/6o9D2wYMHJCQkYDabyZQpU0qWKpKq3bp1i1q1atG+fXv8/PwAOHnyJD4+PsTHxzNw4EAaNWoEKJgSEREReddogvVbKCgoiFmzZrFt2zZcXFyIjIykX79+eHh4sHbtWgVTIv9D0gvj7du3s3LlSq5du0bNmjVp3rw5efLkeWEw5eTkpBfTIi/g5OREfHw86dKlA54Gu8WLF2fmzJl89NFHBAQEEB0dTdu2bfUzJCIiIvKO0Rqtt9DFixepXbs2FStWJG/evJQtW5bg4GCyZ89O27ZtCQsLs3aJIqmawWBgzZo1NGnShLRp05IrVy42btxIt27dOHfuHEajEbPZ/Nw9IvJUUhO22WwmJiYGR0dHLly4YDlnNpspVqwY1atX5+TJk6xfv57o6GhrliwiIiIiVqBQ6i0UHR3NyZMnLZ/Hx8eTJUsWunXrxqlTp6hRowZnzpyxYoUiqUt8fHyyz48ePcrAgQOZOnUqgYGBjBw5krCwME6dOkWHDh04f/78C4MpkXddUhh1//594OmcwyxZsuDr60tAQAALFizAxsbG0mmYLVs2Ro0aRUBAgKWTSkRERETeHVq+9wb7o9k2Xbp0YcuWLQwbNozRo0dja/v0f3P27Nnp0aMHadKkIX/+/Cldrkiq5OfnR0JCAoGBgZZj0dHRVKhQga5du3Lp0iVq1aqFp6cntWrVon///nh5eTFjxgwKFSpkxcpFUh+DwcD69esZN24cDg4OlC5dmiFDhljC3M6dO3PkyBFy5MjB1atXWbFiBSdOnCBHjhzWLl1ERERErECDzt9Qzw6DXblyJefPn6dkyZJUqVIFBwcHxowZw5YtW6hcuTIjR44kIiICX19fChYsyJQpU4DkO/WJvKu2b99OtmzZKFWqVLKg9/z58+TPn58WLVrg4ODAokWLMBgMfPTRR4SFhVGpUiXWr1+PyWTS0j2R/3fkyBGqVKnCgAEDuHz5MmfPngVg9erVZMmShaVLlxIUFMTDhw9xcHBg+vTp2qlSRERE5B2mUOoN9GwgNWTIEKZPn07RokUJDQ2la9euDBgwAGdnZ2bNmkVQUBDXr18nW7ZsZMiQgUOHDmEymaz8CERSn61btxISEsLkyZNxdHQEIDIykmrVquHn50eHDh14+PAhPXv25IMPPsDT05Ps2bNbuWqR1CM8PJwjR45w48YNBg0aRGJiIps3b2bs2LGYzWbWrFlDtmzZuHPnDunTpycmJkZL9kRERETecZop9QZKCqSOHj3K0aNH2b59OwcPHiQkJITdu3fzxRdfcO3aNfz8/Dhx4gRLlizhm2++ITQ0FJPJ9Nz8HBF5Olfq66+/ZsCAATx+/BgAOzs7smXLxsaNGzl8+DBjxozhyJEjNGvWTIGUyDOuXr1Kjx498PX1JSEhAXj6XFWvXj38/f2xsbGhWbNm3Lx5k/feew9bW1sFUiIiIiKiTqk31cyZM9m1axdms5klS5ZgZ2cHwJo1axgyZAgffPABvXv3ply5csnu05I9kaeSOg5v3LiBo6Mj6dOnZ8uWLTRt2pSOHTsyZcoU7O3tmTdvHkFBQVy6dAknJydWrVpF2bJlrV2+SKoSGxvL/PnzmTlzJmnSpGHPnj2W56XExES2bt2Kn58fOXLkYPPmzS+chygiIiIi7x6FUm+oadOmMWjQIFxcXNi0aRNFihSxnFu7di1Dhw6lQIECTJw4UcOYRX4nKZBat24ds2bNon379jRp0oQ0adKwefNmPD09ad++PUFBQQBcunSJ27dvkzNnTnVIifyB2NhYli9fzuTJkylYsCCLFi0iTZo0wNOfuR07duDm5ka+fPmsW6iIiIiIpBoKpd4Az86QetbixYv5/PPPadeuHb1798bV1dVybvny5WzYsIFFixbpHWmRF1izZg1t27Zl5MiRtGzZMtkL5Q0bNtC8eXM6d+7MlClTLB0fIvLbc9KxY8c4fvw4adKkoWjRohQvXpzHjx+zZMkSgoKCyJMnD4sXL7YEUyIiIiIiv6dQKpV7djewiIgIYmJiyJ07t+X87Nmz+fLLL2nbti09e/Z84TvQz34NEYGLFy/SoEED+vTpg5eXF/Hx8cTFxREaGkru3LnJly8fGzdupFGjRvTr149JkyZphz0RfgukQkJC6N27N87Oztjb2/PgwQOmTZtGjRo1iImJYenSpXzzzTc4OTmxbt06HBwcrF26iIiIiKRCttYuQP7Ys2HSqFGj2Lp1KydOnMDT05NmzZrRqFEjvL29SUxMZOzYsRiNRrp27Yqbm1uyr6NASiQ5Gxsb7OzsyJcvH48fP2by5Mls3LiRs2fPWnYMa9iwIdu2bSNXrlwKpET+n8FgYNeuXXh5eTF69Gi8vb3Zvn077u7ueHp68u2339KwYUPatWvH48ePWb16NREREeTKlcvapYuIiIhIKqROqTfA8OHDmTNnDv/+978pVKgQnTt3JmPGjHTr1o327dsDMGfOHHr27MnUqVPx8fGxcsUi1vf7DsGkzxMTE7l06RLt27fHwcGBo0ePUqVKFSpVqkTt2rXx9vbG3d2dESNGWLF6kdTp8ePHDBkyBAcHB8aOHcvVq1epXLkyVapUsQS6q1evpkaNGjx+/JjY2FgyZMhg7bJFREREJJVSKJXK7d69Gx8fH2bMmMHHH3/Mjz/+SM2aNSlYsCD29vb079+fNm3aAE9n5DRq1Ei764n8v1OnTrF48WK6d+9Onjx5knU87dq1i+PHjxMfH8+nn35KlixZAKhbty4NGjSgb9++VqpaJHU7fvw49+7do3Tp0tSqVYvSpUszZ84ctmzZQoMGDQDYuHEj9evXt3KlIiIiIpLaafleKpczZ058fHz4+OOP2b59O23atCEoKAh3d3fef/99pk+fzp07d/Dx8aFJkyYAJCQkKJiSd96TJ09o3749hw8fZuXKlXh4ePDBBx/QokULAKpVq0aVKlWwtX36azAmJoYxY8Zw/PhxZs6cac3SRVK1999/H3ga7BqNRgYNGgSAs7MzjRo1omjRouTPn9+aJYqIiIjIG0KhVCqXJ08eWrVqRVxcHNOnT8fHx4d//etfGI1GihUrxn//+1/Onz+fbIc+BVIiYDKZaNGiBW3atKFEiRLs27cPLy8v1q1bR6VKlejRo4clkFq8eDE7duxg+/btbNq06bm5bCLyvPv373Pw4EEiIyPJnz8/q1atws7ODn9/f9KlS2ft8kRERETkDaAJ2KmcyWQiU6ZMJCQkcP36dezt7TEajcTHx5MrVy7mzp1LQEAABoMBrcQUSa5ChQqMHDmSTJkyMXLkSE6ePImbmxt+fn5UrlyZr7/+mitXrlC+fHny58/Prl27KFOmjLXLFnkjlC9fHk9PT2rWrEnVqlWZOnUqw4YNUyAlIiIiIi9NM6XeEPfu3aNNmzYYjUbKli3LgQMHiIyM5PDhwxiNxueGOovIU35+fly/fp25c+fi4OBA69atCQsLo2LFipw/f54DBw4wbdo0OnfujJ2dnbXLFUmV/ug55uTJk+zevZtbt27Rpk0bChUqZIXqRERERORNpVDKyv7oD/1nl+MlCQ8PZ8iQIURHR5MxY0ZWrVqFyWRSICXyJ1atWkVgYCB79+6le/fubNiwgR07dlC8eHFOnz7Nli1bqF27NsWLF7d2qSJWl/Tcc+7cOSIiIrC3t6dkyZIYjcZk8wpf9BwlIiIiIvJXKZSyomfDpPDwcMsf/aVKlfrDax8+fIjJZMJkMmEwGIiPj7fMxRGRF6tWrRp79+7FxcWFTZs2vfBnTORdlxQ0rV69Gn9/f6Kjo8mRIwd58+ZlyZIlmEymP3zOUUglIiIiIn+H2musJDEx0RJIDR06lE8//RRPT08aNmzIoEGDiI2NTXZ90rVOTk7Y2dlZZkgpkBL5Y0mZ+8CBA3Fzc2PGjBmUKlVK89dEXsBgMLBt2zY6dOhAr169CAsLo2vXrqxatYr69esTFxeHra0tCQkJL7xXREREROSvUihlJUl/wI8fP55Zs2Yxa9YsQkNDadmyJRMnTuTUqVMv/TVE5MWSfkbKlSuH2WwmNDQ02XER+U1kZCRff/01w4YNo2fPnsTHxzN69GgaNmzIr7/+yieffMKTJ0+wsbF5YTAlIiIiIvJXKZSyovj4eI4cOcLUqVOpUqUK3333HfPnz2fWrFmUKlWKuLg4a5co8lZwdnZmxIgRTJ48mYMHD1q7HJFUKXPmzLi7u1OzZk0iIiKoXbs2DRs2ZM2aNXTs2JFdu3bx4YcfWoIpEREREZF/SqGUFcXExLBv3z4yZ87MDz/8QPv27Rk3bhxeXl7ExcUxevRo9uzZY+0yRd4KNWrUoEKFCuTIkcPapYikCknLWE+cOMH+/fsB6NChA+XKlWPjxo2WMNfGxob8+fNTtWpVMmfOzJUrV6xZtoiIiIi8RRRKpRCz2fzcsXTp0tG6dWtmzJhBw4YNmTJlCt7e3gDcuXOHw4cP88svv6R0qSJvpZw5c7J582Zy5cpl7VJErC5pMHlISAgeHh5s2LCBS5cuWc6fO3eOU6dO4eLiAsDRo0epUKECa9aswdXV1Vpli4iIiMhbRlOyU8Czu+xdu3aNhIQEcufODUCVKlVYunQp1atXp379+gDcvn2bLl268ODBAzp27GitskXeOg4ODtYuQcQqfr87nsFg4Pvvv+df//oXgYGBtG7dmgwZMljO169fn5CQECpUqICrqyubNm3i4MGDODo6WqN8EREREXlLGRK1DVWKGTp0KMuXLyc6Opp8+fIxYMAAmjVrxpw5cwgICMDR0ZFMmTLx+PFjnjx5woEDBzCZTCQkJGh+h4iI/G3Xr18ne/bswNOAymw24+vrS0JCArNnz7aEVknPN48ePWL79u385z//wcbGhgEDBlCiRAkrPwoREREReduoU+o1erZDasGCBQQFBREYGIizszMzZ85k5MiRXLt2jd69e1O4cGFOnDjB5cuXKVq0KJ9++im2trbEx8dja6v/TSIi8vfMmDGDvXv3smDBAuzs7DAYDNjY2HDp0iUyZ84M/LYjZdIbIBEREXh4eODh4UFcXBx2dnZWq19ERERE3l7qlEoB69ev5/r16xiNRrp27Wo53rt3b7Zu3cqSJUuoUKHCc/epQ0pERP6pVatWUbp0adzc3Hjw4AFp06blyZMndOjQgWvXrrFjxw7Lc01iYiKRkZGMGzeOtm3bUq5cueeW/omIiIiIvCoadP6aXbx4kWbNmuHt7c2NGzcAiI+PB2D69OmkTZuWadOmvfBeBVIiIvJPNW/eHDc3N3766Sfatm3LkSNHMJlM+Pv7c+jQIXr37k1cXBzwtGMqMDCQrVu3WnaqVCAlIiIiIq+LQqnXLFeuXGzcuBE3Nzd27NhBXFwctra2lt34KlWqZHkxICIi8iolPdfcu3ePq1evcvPmTcaOHcuxY8coXrw43377LYsXL6Z69eq4u7vTvHlzZs2axeLFiy0zqEREREREXheFUq9Q0h//z35ua2tLnTp1mDFjBuHh4bRu3Zp79+4RFxdHfHw8hw4dIn369FaqWERE3mZGo9GyRLxmzZr069ePO3fuMHLkSE6ePEnTpk0JDw+nYsWKZM2alQIFCrB//37KlClj7dJFRERE5B2gmVKvyLNDzb/66ivCwsI4e/YsnTp1onLlyri5ubFt2zbatWvHe++9R4ECBUifPj3h4eGEhYVhMpms/AhERORtkTQH6uHDh3Tr1o1y5crRv39/AJYvX86cOXPIkCEDw4cPp0yZMpYZhpofJSIiIiIpSZ1Sr0hSIDVw4EBGjhxJmjRpyJgxIyNHjsTf35/Q0FDq1q3L0qVLMRqNnDx5kuHDh/Pzzz9jMpksc6ZERET+KYPBwJ49e/jkk0+4f/8+Hh4elm7e1q1b0717d+7fv8+YMWM4cuSIZhiKiIiIiFUolHqFfvrpJ/7zn/+wfv16pk2bxurVqwkICODu3btMmzaNyMhIqlevzvTp03n48CGjRo2y3KsXBCIi8qokJiZy69Ytbt26xd69e8mQIQNGo5HHjx8DT4MpLy8vLly4QGBgYLJB5yIiIiIiKUWh1D/w+xlSBoOBmJgYHB0dLceaNWtGx44d2bBhA9euXcNkMlGzZk2WLVvG7t27qV+/vuVeERGRV8FgMFCvXj0CAgJIkyYN7dq1A8DBwcESQLVs2RJ/f3/GjBmDnZ2dNcsVERERkXeUQql/IGnJ3rBhw1i2bBkPHz4kMTGRO3fuAPDkyRMA2rZtS9q0adm1a5flvtq1azN37lx++eUXrl69ap0HICIib4Wk8ZBnzpzhp59+4qeffsJgMODh4UFQUBCnT5/Gw8MDADs7O2JjYwHw9PQkb968VqtbRERERN5tCqX+hmc7pDZt2kRgYCCFCxemRo0aVK1alY4dO3Lx4kXL8PJbt26RLl06cuTIYbnPYDDQoEEDjh8/Ts6cOVP8MYiIyNshaTh5SEgItWrVolevXjRt2pR27dqxdetWGjduzJQpUzhx4gSenp4A2NvbW7lqERERERHtvvePzJ8/n7i4OGJiYujbty8AERERtG7dmrCwMAYMGICTkxPr16/nxo0bHD58WLOjRETklfvxxx9p2LAhY8aMoWfPnqxYsYK2bdsydepUevXqRWxsLFu2bKFDhw40aNCApUuXWrtkERERERGFUn9XREQEFStW5MKFC3z22WdMmjTJci4uLo7PP/+cgwcPEh8fT/78+VmyZAkmk8my7baIiMg/ldQlNX78eA4fPsyqVau4ePEitWrVok6dOsyePRuAe/fukS5dOrZs2ULhwoVxc3OzcuUiIiIiIgql/haz2YzRaCQ8PJw+ffpw5coV9uzZg4uLS7LQKSoqCltbWxwdHTEYDMTHx2Nra2vl6kVE5G2RFEoNHz4cg8HAgAEDKFy4MO7u7syaNQuDwcC6deu4ffs2nTt31qYaIiIiIpKqaKbUXzR27FhGjRpFXFwcJUuW5KuvvsLR0ZF69eoRHR2NjY2NZcB5+vTpcXJywmAwkJiYqEBKREReqaSQKUeOHAQEBODq6kqrVq2YMWOG5dyaNWs4dOiQZbi5iIiIiEhqoVDqLzKZTIwePZqpU6cSFxdH8eLFWbp0KWazmY8//pjo6GhMJpPl3eskendaRET+qWd32Tt69Cg3b94EwNvbmxYtWhAdHU2XLl2wsbEhOjqawYMHs2nTJvr27YuDg4M1SxcREREReY5ad/5E0jK9Z/n5+eHk5ESvXr0wm83069eP4sWLs2zZMj799FMKFy7M2bNncXR0tFLVIiLytjIYDAQHB+Pr68ujR48oU6YMzZs3p2fPngwZMoTr169TtmxZ3n//fRwcHLhw4QKbN2+mSJEi1i5dREREROQ5CqX+RFJ3088//0yxYsUsx3v27InZbMbX1xeAvn37Urx4cRYsWMC///1vbbUtIiKvXGJiIrdv32bChAl8+eWXFCxYkLlz57Jo0SKio6MZOHAg27ZtY9GiRdy8eZPs2bPz0UcfkTdvXmuXLiIiIiLyQgqlXuDw4cOUL18eg8HAzp07qVWrFosXL6Zdu3aWa3r16kVMTAz+/v6kTZuWzp07U6pUKRYtWgSgXfZEROSVSFoOnpiYiIODA66urjRt2pSMGTNSuHBhxo4dS3BwMPHx8fj7+9O+fXtrlywiIiIi8lI0U+p3Zs+eTePGjTl16hQANWrUoH///nTr1o2lS5cmu7ZRo0akSZOG3r17ExwcnOycAikREXkVDAYDmzZtolGjRnTt2pWrV6+SMWNGALJmzcrgwYOpXLkymzdvZvDgwdYtVkRERETkL1Ao9Yw5c+bg4+PDjBkzks3fCAgIwMfHh44dOyYLpuzt7enVqxcrVqygdevW1ihZRETecvv27aNx48a4uLhw7do1jhw5Qu/evS3ns2XLhr+/P0WKFOHw4cNERkZasVoRERERkZdnSEzayucdFxQUZAmYPD09LccPHDjAhx9+CMCgQYMICAhg+PDhFC9enEWLFpGYmMj69esBiI+Px9ZWKyJFROTVOHnyJOfPn+f8+fP06dOHiIgI5s2bx5IlS6hduzaBgYGWayMiIkhISMDZ2dmKFYuIiIiIvDwlKMCaNWvo0aMHa9eupVGjRpbjHh4eZMqUiZIlS+Lo6Mj48eNxdnYmMDCQtGnTki1bNr777jvL9QqkRETkVbl27Rp169blzp07jBkzBoAsWbLQpUsXABYvXoyfnx8BAQGWcyIiIiIib5J3fvlebGwsW7duJX/+/Fy4cMFyvHnz5pw5c4aRI0fi6OhIUkNZv3792L17Nzt27GDnzp2YTCbi4+OtVb6IiLzhnm1YfvbjdOnSMWrUKLJnz87u3bstx7NkyULXrl3p2LEjS5YsYejQoSlar4iIiIjIq/LOt/bY29szfPhw7O3tWbZsGYmJiezdu5czZ86wYcMG8uXLl2znI4PBgKurq+V+s9msDikREflbzGYzRqORu3fvYjabyZw5s+VcunTpaN26NXZ2dvTr14+uXbsyd+5cADJnzkyHDh0wmUzJOnxFRERERN4kmin1/27cuMGYMWPYuHEjUVFRhIeHkzNnTp48eYLJZAKgYcOGlCxZknHjxlm5WhEReVucP3+ejz76iMyZM/PFF19QsmRJ8ufPbzkfHR1NSEgIgwYNomHDhpZgCn4LtURERERE3kRq8fl/Li4uDB06FKPRyL59+1i2bBmff/45JpOJhIQEGjduzNmzZ1mzZo21SxURkbfIpUuXcHV1pVatWowbN46sWbNSqFAhhg4diqOjI+nSpaNJkyYA+Pv706pVK1asWAGgQEpERERE3mj6a/YZzs7ODB48mEqVKrFy5UomTZoEgKenJ+fOnePEiROaISUiIq9UsWLFiIqKokSJEnz33Xd4eXnx3Xff0b59e3x8fDh37hzp06enQ4cODB8+nGPHjnH9+nVrly0iIiIi8o9p+d4L3Lhxg7FjxxIaGsrZs2fJmDFjskBKM6RERORVSJpVuHz5cgIDA/n2228pVKgQAAULFuTq1as4OjrSvHlzKleuTPv27YmKiiJDhgxWrlxERERE5J9Tp9QLuLi4MGTIENzc3ChXrpwCKREReS0MBgMA5cqVw9HRkYsXLwLQuXNnHj16xOnTp5kyZQr37t2jf//+3L59W4GUiIiIiLw11Cn1J+7evUuGDBkwGo0KpERE5LXy8/Pjhx9+oFChQuzcuZO1a9dSoUIFAKKiojCbzWTKlMnKVYqIiIiIvDoKpV6CdjcSEZHXJek55sqVK9SqVYsnT54QHBxMmTJlrF2aiIiIiMhrpaTlJSiQEhGR1yXpOcbZ2ZmKFSuSJ08eSyCl941ERERE5G2mtEVERMTKEhMTMZlMDBs2jPDwcBYsWAD8NnNKRERERORtpFBKRETkNUrqdoqJieHRo0cvvMZgMJCYmEiePHkoW7YsO3bsIDY2NiXLFBERERFJcZopJSIi8pokJiZiMBhYv349QUFB3L59m06dOtGiRQsyZ878wnvWr19P4cKFKVSoUApXKyIiIiKSshRKiYiIvGJJYRTA7t27cXd359NPPyUuLo4FCxbg7e1N//79cXV1feE9IiIiIiLvAoVSIiIir8nVq1cJDg4mPj6ezz77DHjaCdW+fXvatGmDn59fsmBKRERERORdoplSIiIir8D06dPZsWMHAAkJCVy+fJncuXMzcuRIzGaz5bpGjRqxcOFCli5dSmBgIGfPnrVWySIiIiIiVqVQSkRE5B9ITEwkOjqakJAQ8ubNC4CNjQ158uRhzpw53Lt3j7CwMCIjIy33NG7cmMWLFzNjxgxmz55NfHy8tcoXEREREbEaLd8TERH5B5JmQSUkJGBjY8OBAwe4c+cO9erVw2g0MnfuXLp3786IESPw9fUlU6ZMlns3b95M/vz5KVy4sBUfgYiIiIiIddhauwAREZE3VdL7OvHx8dja2hIfH0/fvn158uQJRqORunXr0rVrV+Lj4+nZsycAffr0IWPGjADUr1/fWqWLiIiIiFidQikREZG/KKk7KioqiowZM2Jra8uePXtwdnZm7dq1eHp6Mm7cOMxmM/Xq1cPb2xsAX19fHj58iL+/PxkyZLDyoxARERERsS7NlBIREfmLDAYDERERlC5dmsWLF7Nt2zaqV6/OhQsXcHZ2JiQkhNjYWCZMmMCWLVswm814e3sTEBDA119/zZMnT6z9EERERERErE4zpURERP6GGzduMGfOHCZNmkRcXBzLli2jadOmxMbGYm9vz82bN/Hw8MDe3p7BgwdTt25djEYj9+7dsyzfExERERF5l6lTSkRE5G9wcXHhww8/5MGDBwBER0cDYG9vT1xcnGUpX0JCAgMGDGDHjh0AWrYnIiIiIvL/FEqJiIj8RQkJCQAULFiQNWvWMHToUHr16kVQUBAAdnZ2lmAqJCSEnDlzUrBgQeDp0j8REREREdGgcxERkZeWNODcbDZjY2ODq6srrq6ulCpVisePH+Pn54fRaKRbt27Y2dmxbNkyypYty6ZNmxRGiYiIiIj8jkIpERGRl5AUSH3//fcsXryYuLg4cufOzfjx48mbNy9eXl4A9OvXjwsXLpCQkMC0adM4ceKEAikRERERkRfQ8j0REZGXYDAYWL16NU2bNsVkMpE7d25WrFiBh4cHCQkJ5M6dGx8fH0aPHk1wcDB79uxh3759FChQwNqli4iIiIikStp9T0RE5CWEh4fTokUL+vbtS48ePbh48SKVK1fmxo0bVKlShZ07d2Jr+7QBOTo6moSEBO2yJyIiIiLyJ9QpJSIi8hKuXr1Kw4YN6dGjB7/++iu1atXC3d2d7777jmPHjtGyZUvi4uIASJcunQIpEREREZH/QZ1SIiIiLyksLIySJUvi6emJk5MTixcv5tGjR1SvXp3Q0FDq1q3Lli1brF2miIiIiMgbQZ1SIiIigNls/sPPk96/KVWqFHfv3uXy5cs0b94cg8GAra0tpUuXZsOGDcyaNStFaxYREREReZMplBIREQGMRiOXL19m2rRpls+Tgqlnd89LkyYN9+7dY+nSpVy8eJGhQ4eye/duypYti6urq1VqFxERERF5E9lauwAREZHUICEhgZkzZ7J69WpiY2Px8/OzBFNG49P3cMxmM2nSpCEwMBAvLy+qVq2K0Whk7dq1uLi4WPkRiIiIiIi8WTRTSkRE5P9dvXqVgIAADhw4QNOmTRk4cCBAsmAKngZYERERXLt2jezZsyuQEhERERH5GxRKiYiIPOPGjRuMGTOGQ4cOvTCYiouLY+LEieTIkYPOnTtbuVoRERERkTeXZkqJiIg8w8XFBX9/fypUqMDq1asZP3488HTGVExMDP369eOLL77gww8/tHKlIiIiIiJvNoVSIiIiv/NsMLVmzRomTJiA2WzG39+fhQsXcuDAAYoVK2btMkVERERE3mhaviciIvIHkpbyHTlyhPv373Pu3Dn27t1L2bJlrV2aiIiIiMgbT51SIiIifyCpY6pw4cLExsayf/9+BVIiIiIiIq+IOqVERET+h9u3b2M2m3F2drZ2KSIiIiIibw2FUiIiIiIiIiIikuK0fE9ERERERERERFKcQikREREREREREUlxCqVERERERERERCTFKZQSEREREREREZEUp1BKRERERERERERSnEIpERERERERERFJcQqlREREREREREQkxSmUEhEREXkLVK9enerVq1u7DBEREZGXplBKRERE5BVZsGABBoPB8p+trS05c+akY8eOXL161drliYiIiKQqttYuQERERORt88UXX+Dq6srjx485cOAACxYsYO/evZw4cQIHB4fX8j23bdv2Wr6uiIiIyOuiUEpERETkFatfvz7ly5cHoGvXrmTJkoUJEyawbt06WrZs+Vq+p52d3Wv5uiIiIiKvi5bviYiIiLxmH330EQDnzp2zHDt16hTNmzfnvffew8HBgfLly7Nu3brn7g0PD6datWqkSZOGXLly8eWXXzJ//nwMBgMXL160XPeimVK3bt2iS5cuODs74+DgQKlSpVi4cGGyay5evIjBYGDSpEnMmTOHAgUKYG9vT4UKFTh06NCr+0cQERER+R11SomIiIi8ZknhUaZMmQA4efIkVapUIWfOnAwaNAgnJyf+85//0KRJE4KDg2natCkAV69epUaNGhgMBgYPHoyTkxNz587F3t7+f37PmJgYqlevztmzZ+nVqxeurq6sXLmSjh07cu/ePfr06ZPs+qVLlxIdHY2XlxcGg4GJEyfi6enJ+fPnMZlMr/YfRERERASFUiIiIiKvXFRUFBERETx+/JiffvqJUaNGYW9vj7u7OwB9+vQhT548HDp0yBIw9ezZk6pVqzJw4EBLKDVhwgTu3r3LkSNHKF26NACdOnWiYMGC/7OGOXPm8N///pdvv/2Wdu3aAeDt7U21atUYOnQonTt3Jl26dJbrL1++zJkzZyzBWeHChfHw8GDr1q2WukVEREReJS3fExEREXnFateuTdasWcmdOzfNmzfHycmJdevWkStXLu7cucP3339Py5YtiY6OJiIigoiICCIjI/nkk084c+aMZae+LVu2UKlSJUsgBfDee+9ZQqY/s2nTJlxcXGjTpo3lmMlkwtfXlwcPHrBr165k17dq1coSSMFvSw7Pnz//T/4pRERERP6QOqVEREREXrEZM2ZQqFAhoqKi+Oabb9i9e7elI+rs2bMkJiYybNgwhg0b9sL7b926Rc6cObl06RKVKlV67rybm9v/rOHSpUsULFgQozH5e5BFixa1nH9Wnjx5kn2eFFDdvXv3f34vERERkb9DoZSIiIjIK/bBBx9Ydt9r0qQJVatWpW3btpw+fRqz2QzA559/zieffPLC+18mdHrVbGxsXng8MTExhSsRERGRd4VCKREREZHXyMbGhnHjxlGjRg2++uorOnfuDDxdSle7du0/vTdv3rycPXv2ueMvOvaie8PDwzGbzcm6pU6dOmU5LyIiImJNmiklIiIi8ppVr16dDz74gClTppA+fXqqV69OUFAQ169ff+7a27dvWz7+5JNP2L9/P8eOHbMcu3PnDkuWLPmf37NBgwbcuHGDFStWWI7Fx8czffp00qZNS7Vq1f7ZgxIRERH5h9QpJSIiIpIC/Pz8aNGiBQsWLGDGjBlUrVqV999/n27dupE/f35u3rzJ/v37uXLlCmFhYQAMGDCAb7/9ljp16tC7d2+cnJyYO3cuefLk4c6dOxgMhj/8ft27dycoKIiOHTsSGhpKvnz5WLVqFfv27WPKlCnJdt4TERERsQaFUiIiIiIpwNPTkwIFCjBp0iS6devG4cOHGTVqFAsWLCAyMpJs2bJRpkwZhg8fbrknd+7c7Ny5E19fX8aOHUvWrFnx8fHByckJX19fHBwc/vD7pUmThh9++IFBgwaxcOFC7t+/T+HChZk/fz4dO3ZMgUcsIiIi8ucMiZpeKSIiIvJG6du3L0FBQTx48OAPB5SLiIiIpHaaKSUiIiKSisXExCT7PDIyksWLF1O1alUFUiIiIvJG0/I9ERERkVSsUqVKVK9enaJFi3Lz5k3mzZvH/fv3GTZsmLVLExEREflHFEqJiIiIpGINGjRg1apVzJkzB4PBQNmyZZk3bx4ff/yxtUsTERER+Uc0U0pERERERERERFKcZkqJiIiIiIiIiEiKUyglIiIiIiIiIiIpTqGUiIiIiIiIiIikOIVSIiIiIiIiIiKS4hRKiYiIiIiIiIhIilMoJSIiIiIiIiIiKU6hlIiIiIiIiIiIpDiFUiIiIiIiIiIikuIUSomIiIiIiIiISIr7P2dNaLBRta5kAAAAAElFTkSuQmCC\n" + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "UNESCO Sites Distribution by Region:\n", + "Europe and North America : 524 sites\n", + "Asia and the Pacific : 266 sites\n", + "Latin America and the Caribbean: 141 sites\n", + "Africa : 96 sites\n", + "Arab States : 86 sites\n" + ] + } + ], + "source": [ + "if conn:\n", + " # Create a heatmap of UNESCO sites by region\n", + " try:\n", + " cursor.execute(\"\"\"\n", + " SELECT REGION_EN, COUNT(*) as site_count\n", + " FROM UNESCO_SITES_COPY\n", + " GROUP BY REGION_EN\n", + " ORDER BY site_count DESC\n", + " \"\"\")\n", + "\n", + " rows = cursor.fetchall()\n", + "\n", + " # Create bar chart\n", + " import matplotlib.pyplot as plt\n", + "\n", + " regions = [row[0] for row in rows]\n", + " counts = [row[1] for row in rows]\n", + "\n", + " plt.figure(figsize=(12, 6))\n", + " plt.bar(regions, counts, color='skyblue')\n", + " plt.title('UNESCO World Heritage Sites by Region', fontsize=16)\n", + " plt.xlabel('Region', fontsize=12)\n", + " plt.ylabel('Number of Sites', fontsize=12)\n", + " plt.xticks(rotation=45, ha='right')\n", + " plt.tight_layout()\n", + " plt.show()\n", + "\n", + " print(\"\\nUNESCO Sites Distribution by Region:\")\n", + " for region, count in zip(regions, counts):\n", + " print(f\"{region:30}: {count:4} sites\")\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error creating visualization: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_4ThBjcTUEVP" + }, + "source": [ + "## Section 11: Advanced Geographic Queries\n", + "\n", + "Demonstrate more complex geographic queries with SelectAI." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jtTPkWbjUEVQ", + "outputId": "b25af635-3738-4921-d94e-306d9ae788ed" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "================================================================================\n", + "ADVANCED GEOGRAPHIC QUERIES\n", + "================================================================================\n", + "\n", + "Advanced Query 1: Find UNESCO sites within 10 degrees latitude of the equator\n", + "======================================================================\n", + "Generated SQL:\n", + "SELECT \"ID\", \"NAME_EN\", \"REGION_EN\", \"STATES_NAME_EN\", \"CATEGORY\", \"LONGITUDE\", \"LATITUDE\"\n", + "FROM \"DEMOUSER\".\"UNESCO_SITES_COPY\" us\n", + "WHERE ABS(\"LATITUDE\") <= 10\n", + "\n", + "Results: 101 sites found near equator\n", + " ID NAME_EN \\\n", + "0 36 Mbanza Kongo, Vestiges of the Capital of the f... \n", + "1 61 Royal Palaces of Abomey \n", + "2 89 Central Amazon Conservation Complex \n", + "3 91 Brazilian Atlantic Islands: Fernando de Noronh... \n", + "4 220 Los Katíos National Park \n", + "\n", + " REGION_EN STATES_NAME_EN CATEGORY LONGITUDE \\\n", + "0 Africa Angola Cultural 14.249722 \n", + "1 Africa Benin Cultural 1.983333 \n", + "2 Latin America and the Caribbean Brazil Natural -62.008333 \n", + "3 Latin America and the Caribbean Brazil Natural -32.425111 \n", + "4 Latin America and the Caribbean Colombia Natural -77.000000 \n", + "\n", + " LATITUDE \n", + "0 -6.268889 \n", + "1 7.183333 \n", + "2 -2.333333 \n", + "3 -3.857944 \n", + "4 7.666667 \n", + "\n", + "Advanced Query 2: Compare the number of cultural vs natural sites in each region\n", + "======================================================================\n", + "Generated SQL:\n", + "SELECT \n", + " us.REGION_EN AS \"Region\",\n", + " COUNT(CASE WHEN UPPER(us.CATEGORY) = 'CULTURAL' THEN 1 END) AS \"Cultural Sites\",\n", + " COUNT(CASE WHEN UPPER(us.CATEGORY) = 'NATURAL' THEN 1 END) AS \"Natural Sites\"\n", + "FROM \n", + " \"DEMOUSER\".\"UNESCO_SITES_COPY\" us\n", + "GROUP BY \n", + " us.REGION_EN\n", + "ORDER BY \n", + " us.REGION_EN\n", + "\n", + "Results: Regional comparison completed\n", + " Region Cultural Sites Natural Sites\n", + "0 Africa 53 38\n", + "1 Arab States 78 5\n", + "2 Asia and the Pacific 189 65\n", + "3 Europe and North America 448 66\n", + "4 Latin America and the Caribbean 95 38\n" + ] + } + ], + "source": [ + "if conn:\n", + " print(\"\\n\" + \"=\"*80)\n", + " print(\"ADVANCED GEOGRAPHIC QUERIES\")\n", + " print(\"=\"*80)\n", + "\n", + " # Advanced Query 1: Sites near specific coordinates\n", + " prompt6 = 'Find UNESCO sites within 10 degrees latitude of the equator'\n", + "\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt6}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'showsql'\n", + " ) as generated_query\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " generated_sql_6 = result[0]\n", + "\n", + " print(f\"\\nAdvanced Query 1: {prompt6}\")\n", + " print(\"=\" * 70)\n", + " print(\"Generated SQL:\")\n", + " print(generated_sql_6)\n", + " print()\n", + "\n", + " # Execute the query and show results\n", + " cursor.execute(str(generated_sql_6))\n", + " rows = cursor.fetchall()\n", + " col_names = [description[0] for description in cursor.description]\n", + "\n", + " df = pd.DataFrame(rows, columns=col_names)\n", + " print(f\"Results: {len(df)} sites found near equator\")\n", + " print(df.head())\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error in Advanced Query 1: {e}\")\n", + "\n", + " # Advanced Query 2: Regional comparison\n", + " prompt7 = 'Compare the number of cultural vs natural sites in each region'\n", + "\n", + " try:\n", + " cursor.execute(f\"\"\"\n", + " SELECT DBMS_CLOUD_AI.GENERATE(\n", + " prompt => '{prompt7}',\n", + " profile_name => 'COHERE_UNESCO',\n", + " action => 'showsql'\n", + " ) as generated_query\n", + " FROM dual\n", + " \"\"\")\n", + "\n", + " result = cursor.fetchone()\n", + " generated_sql_7 = result[0]\n", + "\n", + " print(f\"\\nAdvanced Query 2: {prompt7}\")\n", + " print(\"=\" * 70)\n", + " print(\"Generated SQL:\")\n", + " print(generated_sql_7)\n", + " print()\n", + "\n", + " # Execute the query and show results\n", + " cursor.execute(str(generated_sql_7))\n", + " rows = cursor.fetchall()\n", + " col_names = [description[0] for description in cursor.description]\n", + "\n", + " df = pd.DataFrame(rows, columns=col_names)\n", + " print(f\"Results: Regional comparison completed\")\n", + " print(df.head())\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error in Advanced Query 2: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b6rXsvbUUEVQ" + }, + "source": [ + "## Section 12: Handle Connection Errors and Cleanup\n", + "\n", + "Properly close database connections and handle any errors." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bxGbPQGZUEVQ", + "outputId": "27565c40-f4b5-4af7-a21f-979f8fe7ad6a" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "✓ Database connection closed\n" + ] + } + ], + "source": [ + "# Close database connection\n", + "if conn:\n", + " try:\n", + " conn.close()\n", + " print(\"✓ Database connection closed\")\n", + " except Exception as e:\n", + " print(f\"✗ Error closing connection: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Giwigy9VUEVQ" + }, + "source": [ + "## Troubleshooting Guide\n", + "\n", + "### Connection Issues\n", + "- **Wallet not found**: Ensure wallet.zip is uploaded to Google Drive at the correct path\n", + "- **TNS name error**: Verify the TNS name matches your Oracle Cloud Console wallet\n", + "- **Authentication failed**: Check username and password are correct\n", + "\n", + "### SelectAI Issues\n", + "- **Cohere API errors**: Verify API key is valid and not expired\n", + "- **Permission denied**: Ensure ADMIN user granted DBMS_CLOUD_AI privileges\n", + "- **Profile not found**: Check that profile was created successfully\n", + "\n", + "### API Key Security\n", + "- Never commit real Cohere API keys to repositories\n", + "- Use environment variables or Google Colab secrets for sensitive data\n", + "- Rotate keys periodically for security\n", + "\n", + "### Geographic Query Tips\n", + "- Use specific region names (e.g., 'Europe and North America')\n", + "- Include coordinate ranges for precise location queries\n", + "- Combine multiple criteria for complex geographic analysis\n", + "\n", + "## Next Steps\n", + "\n", + "1. Customize the natural language prompts for your specific UNESCO data analysis needs\n", + "2. Create additional AI profiles for different geographic analysis scenarios\n", + "3. Integrate results with pandas for advanced data analysis\n", + "4. Build interactive dashboards with Streamlit or other visualization tools\n", + "5. Explore spatial functions for distance and proximity calculations\n", + "\n", + "## References\n", + "\n", + "- [Oracle SelectAI Documentation](https://docs.oracle.com/en/cloud/paas/autonomous-database/index.html)\n", + "- [Cohere API Documentation](https://docs.cohere.ai/)\n", + "- [oracledb Python Driver](https://python-oracledb.readthedocs.io/)\n", + "- [UNESCO World Heritage Centre](https://whc.unesco.org/)\n", + "- [Folium Documentation](https://python-visualization.github.io/folium/)" + ] + } + ], + "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.8.0" + }, + "colab": { + "provenance": [] + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/notebooks/mongodb-api/README-mongodb-api.md b/notebooks/mongodb-api/README-mongodb-api.md new file mode 100644 index 00000000..4c09b0cb --- /dev/null +++ b/notebooks/mongodb-api/README-mongodb-api.md @@ -0,0 +1,568 @@ +# Unlocking Oracle AI Database API for MongoDB with Google Colab + +## Introduction + +Oracle Database API for MongoDB makes it possible to connect to Oracle Autonomous AI Database using MongoDB language drivers and tools. This enables developers familiar with MongoDB to leverage Oracle's converged database capabilities—including JSON storage, SQL querying, and AI features—without changing their application architecture. + +Oracle Database API for MongoDB allows you to use standard MongoDB drivers and tools while accessing Oracle Autonomous AI Database. This lets you manage multiple data types (relational, JSON, graph, vector) within a single database and query them seamlessly with SQL. + +Get ready to experience the power of Oracle AI Database API for MongoDB! + +Perfect for developers and data professionals looking to leverage the flexibility of MongoDB API with the power of Oracle's Autonomous Database. + +💻 #MongoDB API #Oracle #AutonomousDatabase #DataScience #GoogleColab #Notebook + +![Enable ACL](images/developer.jpeg) + +Estimated Time: 20 mins. + +### Objectives + +🚀 In this lab, you'll learn how to: + +* ✅ Enable MongoDB API access +* ✅ Connect using MongoDB drivers (Python/Node.js) +* ✅ Execute MongoDB queries against Oracle data +* ✅ Query the same data using SQL and MongoDB API interchangeably +* ✅ Run MongoDB APIs using Google Colab Notebooks. + +### Prerequisites + +This lab assumes you have: +* Oracle Cloud account with privileges to create and manage Oracle Autonomous AI Database +* Oracle Autonomous AI Database instance 26ai deployed +* MongoDB driver installed (Python, Node.js, SQL Worksheet or Google Colab Notebook) + +Download and Run [Google Colab Notebook](mongodb_create_customers.ipynb). Follow the instructions from Task 1 & Task 6 + +## Task 1: Enable MongoDB API on Your Database + +1. Login to [Cloud.oracle.com](https://cloud.oracle.com) and navigate to **Oracle AI Database** → **Autonomous AI Database**. Ensure you've selected the correct region and compartment. + +2. Click your database instance to open its details page. + +3. Under **Network** settings, change from **Allow secure access from everywhere** to **Allow secure access from specified IPs and VCNs**. + + ![Enable ACL](images/enable-acl1.png) + +4. Add your machine's public IP address to the access control list. + + ![Enable ACL](images/enable-acl2.png) + +5. mTLS authentication will be set to **Required** after the database automatically restarts. + + ![Enable ACL](images/enable-acl3.png) + +6. Navigate to **Tool Configurations** tab and enable **MongoDB API**. + + ![Enable ACL](images/enable-acl4.png) + +7. Copy the MongoDB connection URL displayed. + + ![Enable ACL](images/enable-acl5.png) + + The URL format is: + ``` + mongodb://user:@host:27017/user?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true + ``` + + Where: + - `user`: Your Oracle Autonomous Database username (e.g., `demouser`) + - ``: Your database password (substitute with actual password locally) + - `host`: Oracle Database hostname from console (e.g., `adb-region.oraclecloudapps.com`) + - `authMechanism=PLAIN`: Required for Oracle MongoDB API + - `ssl=true`: Required for TCPS connections + +## Task 2: Connect Using Python MongoDB Driver + +1. Install the MongoDB Python driver: + + ``` + pip install pymongo + ``` + +2. Create a Python script to connect and query data: + + ``` + from pymongo import MongoClient + + # Replace with your MongoDB connection URL from Task 1 + # Connection URL format: mongodb://user:@host:27017/user?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true + connection_url = "mongodb://user:@host:27017/user?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true" + + def create_customers_collection(db): + """Create customers collection with sample data""" + # Drop existing collection to start fresh + if 'mycustomers' in db.list_collection_names(): + db['mycustomers'].drop() + print("✓ Dropped existing mycustomers collection") + + # Create mycustomers collection and insert sample data + customers_data = [ + { + "_id": 1, + "name": "John Smith", + "email": "john.smith@example.com", + "city": "New York", + "phone": "+1-212-555-0100", + "accountStatus": "active" + }, + { + "_id": 2, + "name": "Sarah Johnson", + "email": "sarah.johnson@example.com", + "city": "San Francisco", + "phone": "+1-415-555-0200", + "accountStatus": "active" + }, + { + "_id": 3, + "name": "Michael Chen", + "email": "michael.chen@example.com", + "city": "Seattle", + "phone": "+1-206-555-0300", + "accountStatus": "inactive" + }, + { + "_id": 4, + "name": "Emily Rodriguez", + "email": "emily.rodriguez@example.com", + "city": "Austin", + "phone": "+1-512-555-0400", + "accountStatus": "active" + }, + { + "_id": 5, + "name": "David Wilson", + "email": "david.wilson@example.com", + "city": "Boston", + "phone": "+1-617-555-0500", + "accountStatus": "active" + } + ] + + result = db['mycustomers'].insert_many(customers_data) + print(f"✓ Created mycustomers collection with {len(result.inserted_ids)} documents") + return result + + def query_customer_by_name(db, customer_name): + """Query customer collection by customer name""" + customer = db['mycustomers'].find_one({"name": customer_name}) + + if customer: + print(f"\n✓ Found customer: {customer_name}") + print(f" Email: {customer.get('email')}") + print(f" City: {customer.get('city')}") + print(f" Phone: {customer.get('phone')}") + print(f" Status: {customer.get('accountStatus')}") + return customer + else: + print(f"✗ Customer '{customer_name}' not found") + return None + + def list_all_customers(db): + """List all customers in the collection""" + customers = db['mycustomers'].find() + print("\n✓ All Customers:") + for customer in customers: + print(f" - {customer['name']} ({customer['email']}) - {customer['city']}") + + try: + client = MongoClient(connection_url) + db = client['user'] # Replace 'user' with your database username + + # Verify connection + print("✓ Connected successfully to Oracle Autonomous AI Database") + + # Create customers collection with sample data + create_customers_collection(db) + + # List all customers + list_all_customers(db) + + # Query customers by name + print("\n" + "="*60) + print("Querying customers by name:") + print("="*60) + + query_customer_by_name(db, "John Smith") + query_customer_by_name(db, "Sarah Johnson") + query_customer_by_name(db, "Jane Doe") # This customer doesn't exist + + except Exception as e: + print(f"✗ Connection failed: {e}") + finally: + client.close() + print("\n✓ Database connection closed") + ``` + +## Task 3: Connect Using Node.js MongoDB Driver + +1. Install the MongoDB Node.js driver: + + ``` + npm install mongodb + ``` + +2. Create a Node.js script: + + ``` + + const { MongoClient } = require("mongodb"); + + // Connection URL format: mongodb://user:@host:27017/user?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true + const connectionUrl = "mongodb://user:@host:27017/user?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true"; + + async function connectAndQuery() { + const client = new MongoClient(connectionUrl); + try { + await client.connect(); + const db = client.db("user"); // Replace 'user' with your database username + + // List collections + const collections = await db.listCollections().toArray(); + console.log(`✓ Connected. Collections:`, collections.map(c => c.name)); + + // Test query + if (collections.some(c => c.name === 'customers')) { + const customer = await db.collection('customers').findOne(); + console.log(`✓ Sample customer:`, customer); + } + } catch (err) { + console.error(`✗ Error:`, err); + } finally { + await client.close(); + } + } + + connectAndQuery(); + + ``` + +## Task 4: Connect Using MongoDB Shell + +1. Install MongoDB Shell from [MongoDB documentation](https://www.mongodb.com/docs/mongodb-shell/) + +2. Open terminal and create a connection using the URL from Task 1: + + ``` + + ./mongosh 'mongodb://user:@host:27017/user?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true' + + ``` + + **Note:** If password contains special characters like `#`, URL-encode them (e.g., `#` becomes `%23`) + +3. List databases to verify connection: + + ``` + + > show dbs + user 850.91 KiB + + ``` + +4. Insert sample JSON documents: + + ``` + + { name: "Storage", price: 80, category: "Accessories", inStock: false } + + ``` + + ![MongoDB APIs](images/api1.png) + +5. Find products by category: + + ``` + + db.myproducts.find({ category: "Electronics" }) + + ``` + ![MongoDB APIs](images/api2.png) + +6. Find all documents in products collection: + + ``` + + db.myproducts.find({}) + + ``` + + ![MongoDB APIs](images/api3.png) + +7. Find products above a price threshold: + + ``` + + db.myproducts.find({ price: { $gt: 300 } }) + + ``` + + ![MongoDB APIs](images/api4.png) + + +8. Update product price: + ``` + + db.myproducts.updateOne( + { name: 'Pendrive' }, // filter + { $set: { price: 1119.99 } } // update + ) + + ``` + + ![MongoDB APIs](images/api5.png) + +## Task 5: Query the Same Data Using SQL Worksheet + +Oracle's converged database enables querying MongoDB collections directly using SQL. This demonstrates interoperability: store JSON via MongoDB drivers, query with SQL without migration. + +1. Open **SQL Worksheet** from Oracle Autonomous Database Console + + ``` + + SELECT * FROM user.mycustomers + + ``` + + **Note:** (Please replace `user` with your database username, for example demouser.customers ) + + ![MongoDB APIs](images/view-customers.png) + + ``` + + INSERT INTO user.myproducts (DATA) VALUES + ( + JSON('{"name": "Mouse", "price": 25, "brand": "Logitech"}') + ); + + ``` + +3. Query MongoDB collection using JSON_TABLE and SQL: + + ``` + + SELECT + jt.name, + jt.price, + jt.category + FROM user.myproducts p, + JSON_TABLE( + p.data, '$' + COLUMNS ( + name VARCHAR2(100) PATH '$.name', + price NUMBER PATH '$.price', + category VARCHAR2(50) PATH '$.category' + ) + ) jt + WHERE jt.price > 300; + + ``` + ![Query Results](images/ws1.png) + + **Key Insight:** Store JSON via MongoDB drivers, query with SQL. No ETL or data migration required—single converged database. + +## Task 6: Connect to Oracle MongoDB API Using Google Colab Notebook + +1. Download and Run [Google Colab Notebook](mongodb_create_customers.ipynb) + +2. Install the required libraries + + ``` + + !pip install pymongo + + ``` + +3. Get the Public IP of Google Colab Notebook and Add it to Allowed ACL list + + ``` + + !curl ipecho.net/plain + + ``` + + ![Enable ACL](images/enable-acl2.png) + +4. Check if pymongo is installed + + ``` + + import subprocess + import sys + + try: + import pymongo + print("✓ pymongo is already installed") + except ImportError: + print("Installing pymongo...") + subprocess.check_call([sys.executable, "-m", "pip", "install", "pymongo"]) + print("✓ pymongo installed successfully") + + ``` + +5. Setup Configuration variables. Replace with your MongoDB connection details. Get these values from Oracle Cloud Console → Autonomous Database → Tool Configurations → MongoDB API + + ``` + + # MongoDB connection parameters + MONGO_HOST = "R9NV7IFXZVF7RHN-INDEDUCATION.adb.ap-mumbai-1.oraclecloudapps.com" # Replace with your host + MONGO_PORT = 27017 + MONGO_USER = "demouser" # Replace with your database username + MONGO_PASSWORD = "" # Replace with your actual password + MONGO_DB = "demouser" # Replace with your database username (same as user in most cases) + MONGO_AUTH_MECHANISM = "PLAIN" + + # Construct MongoDB connection URL + connection_url = f"mongodb://{MONGO_USER}:{MONGO_PASSWORD}@{MONGO_HOST}:{MONGO_PORT}/{MONGO_DB}?authMechanism={MONGO_AUTH_MECHANISM}&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true" + + print("✓ Configuration loaded") + print(f" Host: {MONGO_HOST}") + print(f" User: {MONGO_USER}") + print(f" Database: {MONGO_DB}") + + + ``` + +6. Create customers, List customers and View all customers + + ``` + + from pymongo import MongoClient + + def create_customers_collection(db): + """Create customers collection with sample data""" + # Drop existing collection to start fresh + if 'mycustomers' in db.list_collection_names(): + db['mycustomers'].drop() + print("✓ Dropped existing mycustomers collection") + + # Create mycustomers collection and insert sample data + customers_data = [ + { + "_id": 1, + "name": "John Smith", + "email": "john.smith@example.com", + "city": "New York", + "phone": "+1-212-555-0100", + "accountStatus": "active" + }, + { + "_id": 2, + "name": "Sarah Johnson", + "email": "sarah.johnson@example.com", + "city": "San Francisco", + "phone": "+1-415-555-0200", + "accountStatus": "active" + }, + { + "_id": 3, + "name": "Michael Chen", + "email": "michael.chen@example.com", + "city": "Seattle", + "phone": "+1-206-555-0300", + "accountStatus": "inactive" + }, + { + "_id": 4, + "name": "Emily Rodriguez", + "email": "emily.rodriguez@example.com", + "city": "Austin", + "phone": "+1-512-555-0400", + "accountStatus": "active" + }, + { + "_id": 5, + "name": "David Wilson", + "email": "david.wilson@example.com", + "city": "Boston", + "phone": "+1-617-555-0500", + "accountStatus": "active" + } + ] + + result = db['mycustomers'].insert_many(customers_data) + print(f"✓ Created mycustomers collection with {len(result.inserted_ids)} documents") + return result + + def query_customer_by_name(db, customer_name): + """Query customer collection by customer name""" + customer = db['mycustomers'].find_one({"name": customer_name}) + + if customer: + print(f"\n✓ Found customer: {customer_name}") + print(f" Email: {customer.get('email')}") + print(f" City: {customer.get('city')}") + print(f" Phone: {customer.get('phone')}") + print(f" Status: {customer.get('accountStatus')}") + return customer + else: + print(f"✗ Customer '{customer_name}' not found") + return None + + def list_all_customers(db): + """List all customers in the collection""" + customers = db['mycustomers'].find() + print("\n✓ All Customers:") + for customer in customers: + print(f" - {customer['name']} ({customer['email']}) - {customer['city']}") + + print("✓ Helper functions defined") + + ``` + +7. Connect to Oracle Database using MongoDB API Connection string and list customers + + ``` + + try: + # Connect to Database + client = MongoClient(connection_url) + db = client[MONGO_DB] + + # Verify connection + print("✓ Connected successfully to Oracle Autonomous AI Database") + + # Create customers collection with sample data + create_customers_collection(db) + + # List all customers + list_all_customers(db) + + # Query customers by name + print("\n" + "="*60) + print("Querying customers by name:") + print("="*60) + + query_customer_by_name(db, "John Smith") + query_customer_by_name(db, "Sarah Johnson") + query_customer_by_name(db, "Jane Doe") # This customer doesn't exist + + except Exception as e: + print(f"✗ Connection failed: {e}") + print("\nTroubleshooting tips:") + print("1. Verify MongoDB API is enabled in Oracle Cloud Console") + print("2. Check connection URL format and credentials") + print("3. Ensure your IP is in the database access control list") + finally: + if 'client' in locals(): + client.close() + print("\n✓ Database connection closed") + + ``` + + Run the Google Colab Notebook + + ![Run Colab Notebook](images/run.png) + +## Learn More + +* [Oracle Database API for MongoDB Documentation](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/oracle-database-api-mongodb.html) +* [JSON Relational Duality Views Documentation](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/json-relational-duality-views.html) +* [Autonomous AI Database for Developers](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/autonomous-database-for-developers.html) +* [MongoDB Python Driver Documentation](https://pymongo.readthedocs.io/) +* [MongoDB Node.js Driver Documentation](https://www.mongodb.com/docs/drivers/node/) +* [Download All Source Code](https://github.com/madhusudhanrao-ppm/dbdevrel/tree/main/source-codes) +* [Autonomous AI Database Billing](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/autonomous-database-for-developers-billing.html) + \ No newline at end of file diff --git a/notebooks/mongodb-api/images/api1.png b/notebooks/mongodb-api/images/api1.png new file mode 100644 index 00000000..1f2187c8 Binary files /dev/null and b/notebooks/mongodb-api/images/api1.png differ diff --git a/notebooks/mongodb-api/images/api2.png b/notebooks/mongodb-api/images/api2.png new file mode 100644 index 00000000..395940fa Binary files /dev/null and b/notebooks/mongodb-api/images/api2.png differ diff --git a/notebooks/mongodb-api/images/api3.png b/notebooks/mongodb-api/images/api3.png new file mode 100644 index 00000000..94276a98 Binary files /dev/null and b/notebooks/mongodb-api/images/api3.png differ diff --git a/notebooks/mongodb-api/images/api4.png b/notebooks/mongodb-api/images/api4.png new file mode 100644 index 00000000..473e02af Binary files /dev/null and b/notebooks/mongodb-api/images/api4.png differ diff --git a/notebooks/mongodb-api/images/api5.png b/notebooks/mongodb-api/images/api5.png new file mode 100644 index 00000000..00ef775f Binary files /dev/null and b/notebooks/mongodb-api/images/api5.png differ diff --git a/notebooks/mongodb-api/images/developer.jpeg b/notebooks/mongodb-api/images/developer.jpeg new file mode 100644 index 00000000..1e6d3357 Binary files /dev/null and b/notebooks/mongodb-api/images/developer.jpeg differ diff --git a/notebooks/mongodb-api/images/enable-acl1.png b/notebooks/mongodb-api/images/enable-acl1.png new file mode 100644 index 00000000..bdb3cfde Binary files /dev/null and b/notebooks/mongodb-api/images/enable-acl1.png differ diff --git a/notebooks/mongodb-api/images/enable-acl2.png b/notebooks/mongodb-api/images/enable-acl2.png new file mode 100644 index 00000000..09f3062e Binary files /dev/null and b/notebooks/mongodb-api/images/enable-acl2.png differ diff --git a/notebooks/mongodb-api/images/enable-acl3.png b/notebooks/mongodb-api/images/enable-acl3.png new file mode 100644 index 00000000..76fccc40 Binary files /dev/null and b/notebooks/mongodb-api/images/enable-acl3.png differ diff --git a/notebooks/mongodb-api/images/enable-acl4.png b/notebooks/mongodb-api/images/enable-acl4.png new file mode 100644 index 00000000..5bfce3ac Binary files /dev/null and b/notebooks/mongodb-api/images/enable-acl4.png differ diff --git a/notebooks/mongodb-api/images/enable-acl5.png b/notebooks/mongodb-api/images/enable-acl5.png new file mode 100644 index 00000000..74193ab3 Binary files /dev/null and b/notebooks/mongodb-api/images/enable-acl5.png differ diff --git a/notebooks/mongodb-api/images/run.png b/notebooks/mongodb-api/images/run.png new file mode 100644 index 00000000..ca33853a Binary files /dev/null and b/notebooks/mongodb-api/images/run.png differ diff --git a/notebooks/mongodb-api/images/view-customers.png b/notebooks/mongodb-api/images/view-customers.png new file mode 100644 index 00000000..72f7a9c3 Binary files /dev/null and b/notebooks/mongodb-api/images/view-customers.png differ diff --git a/notebooks/mongodb-api/images/ws1.png b/notebooks/mongodb-api/images/ws1.png new file mode 100644 index 00000000..5d373505 Binary files /dev/null and b/notebooks/mongodb-api/images/ws1.png differ diff --git a/notebooks/mongodb-api/mongodb_create_customers.ipynb b/notebooks/mongodb-api/mongodb_create_customers.ipynb new file mode 100644 index 00000000..51658746 --- /dev/null +++ b/notebooks/mongodb-api/mongodb_create_customers.ipynb @@ -0,0 +1,274 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "authorship_tag": "ABX9TyNa9Xl+mPqSrI7C25S+R4ST", + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rw3W9GNl_cKV" + }, + "outputs": [], + "source": [ + "!pip install pymongo" + ] + }, + { + "cell_type": "code", + "source": [ + "!curl ipecho.net/plain" + ], + "metadata": { + "id": "J6RdlhRPB8Xk" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Install MongoDB Python driver\n", + "import subprocess\n", + "import sys\n", + "\n", + "try:\n", + " import pymongo\n", + " print(\"✓ pymongo is already installed\")\n", + "except ImportError:\n", + " print(\"Installing pymongo...\")\n", + " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"pymongo\"])\n", + " print(\"✓ pymongo installed successfully\")" + ], + "metadata": { + "id": "qmrBrhg9_sag" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Configuration - Replace with your MongoDB connection details\n", + "# Get these values from Oracle Cloud Console → Autonomous Database → Tool Configurations → MongoDB API\n", + "\n", + "# MongoDB connection parameters\n", + "MONGO_HOST = \"R9NV7IFXZVF7RHN-INDEDUCATION.adb.ap-mumbai-1.oraclecloudapps.com\" # Replace with your host\n", + "MONGO_PORT = 27017\n", + "MONGO_USER = \"demouser\" # Replace with your database username\n", + "MONGO_PASSWORD = \"\" # Replace with your actual password\n", + "MONGO_DB = \"demouser\" # Replace with your database username (same as user in most cases)\n", + "MONGO_AUTH_MECHANISM = \"PLAIN\"\n", + "\n", + "# Construct MongoDB connection URL\n", + "connection_url = f\"mongodb://{MONGO_USER}:{MONGO_PASSWORD}@{MONGO_HOST}:{MONGO_PORT}/{MONGO_DB}?authMechanism={MONGO_AUTH_MECHANISM}&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true\"\n", + "\n", + "print(\"✓ Configuration loaded\")\n", + "print(f\" Host: {MONGO_HOST}\")\n", + "print(f\" User: {MONGO_USER}\")\n", + "print(f\" Database: {MONGO_DB}\")" + ], + "metadata": { + "id": "fpD3SadH_4Km" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from pymongo import MongoClient\n", + "\n", + "def create_customers_collection(db):\n", + " \"\"\"Create customers collection with sample data\"\"\"\n", + " # Drop existing collection to start fresh\n", + " if 'mycustomers' in db.list_collection_names():\n", + " db['mycustomers'].drop()\n", + " print(\"✓ Dropped existing mycustomers collection\")\n", + "\n", + " # Create mycustomers collection and insert sample data\n", + " customers_data = [\n", + " {\n", + " \"_id\": 1,\n", + " \"name\": \"John Smith\",\n", + " \"email\": \"john.smith@example.com\",\n", + " \"city\": \"New York\",\n", + " \"phone\": \"+1-212-555-0100\",\n", + " \"accountStatus\": \"active\"\n", + " },\n", + " {\n", + " \"_id\": 2,\n", + " \"name\": \"Sarah Johnson\",\n", + " \"email\": \"sarah.johnson@example.com\",\n", + " \"city\": \"San Francisco\",\n", + " \"phone\": \"+1-415-555-0200\",\n", + " \"accountStatus\": \"active\"\n", + " },\n", + " {\n", + " \"_id\": 3,\n", + " \"name\": \"Michael Chen\",\n", + " \"email\": \"michael.chen@example.com\",\n", + " \"city\": \"Seattle\",\n", + " \"phone\": \"+1-206-555-0300\",\n", + " \"accountStatus\": \"inactive\"\n", + " },\n", + " {\n", + " \"_id\": 4,\n", + " \"name\": \"Emily Rodriguez\",\n", + " \"email\": \"emily.rodriguez@example.com\",\n", + " \"city\": \"Austin\",\n", + " \"phone\": \"+1-512-555-0400\",\n", + " \"accountStatus\": \"active\"\n", + " },\n", + " {\n", + " \"_id\": 5,\n", + " \"name\": \"David Wilson\",\n", + " \"email\": \"david.wilson@example.com\",\n", + " \"city\": \"Boston\",\n", + " \"phone\": \"+1-617-555-0500\",\n", + " \"accountStatus\": \"active\"\n", + " }\n", + " ]\n", + "\n", + " result = db['mycustomers'].insert_many(customers_data)\n", + " print(f\"✓ Created mycustomers collection with {len(result.inserted_ids)} documents\")\n", + " return result\n", + "\n", + "def query_customer_by_name(db, customer_name):\n", + " \"\"\"Query customer collection by customer name\"\"\"\n", + " customer = db['mycustomers'].find_one({\"name\": customer_name})\n", + "\n", + " if customer:\n", + " print(f\"\\n✓ Found customer: {customer_name}\")\n", + " print(f\" Email: {customer.get('email')}\")\n", + " print(f\" City: {customer.get('city')}\")\n", + " print(f\" Phone: {customer.get('phone')}\")\n", + " print(f\" Status: {customer.get('accountStatus')}\")\n", + " return customer\n", + " else:\n", + " print(f\"✗ Customer '{customer_name}' not found\")\n", + " return None\n", + "\n", + "def list_all_customers(db):\n", + " \"\"\"List all customers in the collection\"\"\"\n", + " customers = db['mycustomers'].find()\n", + " print(\"\\n✓ All Customers:\")\n", + " for customer in customers:\n", + " print(f\" - {customer['name']} ({customer['email']}) - {customer['city']}\")\n", + "\n", + "print(\"✓ Helper functions defined\")" + ], + "metadata": { + "id": "zeWbuP3gAbj-" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "try:\n", + " # Connect to MongoDB\n", + " client = MongoClient(connection_url)\n", + " db = client[MONGO_DB]\n", + "\n", + " # Verify connection\n", + " print(\"✓ Connected successfully to Oracle Autonomous AI Database\")\n", + "\n", + " # Create customers collection with sample data\n", + " create_customers_collection(db)\n", + "\n", + " # List all customers\n", + " list_all_customers(db)\n", + "\n", + " # Query customers by name\n", + " print(\"\\n\" + \"=\"*60)\n", + " print(\"Querying customers by name:\")\n", + " print(\"=\"*60)\n", + "\n", + " query_customer_by_name(db, \"John Smith\")\n", + " query_customer_by_name(db, \"Sarah Johnson\")\n", + " query_customer_by_name(db, \"Jane Doe\") # This customer doesn't exist\n", + "\n", + "except Exception as e:\n", + " print(f\"✗ Connection failed: {e}\")\n", + " print(\"\\nTroubleshooting tips:\")\n", + " print(\"1. Verify MongoDB API is enabled in Oracle Cloud Console\")\n", + " print(\"2. Check connection URL format and credentials\")\n", + " print(\"3. Ensure your IP is in the database access control list\")\n", + "finally:\n", + " if 'client' in locals():\n", + " client.close()\n", + " print(\"\\n✓ Database connection closed\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TRTKT_2XAqdt", + "outputId": "229852a8-8232-42f5-e643-b0ad42842e84" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "✓ Connected successfully to Oracle Autonomous AI Database\n", + "✓ Dropped existing mycustomers collection\n", + "✓ Created mycustomers collection with 5 documents\n", + "\n", + "✓ All Customers:\n", + " - John Smith (john.smith@example.com) - New York\n", + " - Sarah Johnson (sarah.johnson@example.com) - San Francisco\n", + " - Michael Chen (michael.chen@example.com) - Seattle\n", + " - Emily Rodriguez (emily.rodriguez@example.com) - Austin\n", + " - David Wilson (david.wilson@example.com) - Boston\n", + "\n", + "============================================================\n", + "Querying customers by name:\n", + "============================================================\n", + "\n", + "✓ Found customer: John Smith\n", + " Email: john.smith@example.com\n", + " City: New York\n", + " Phone: +1-212-555-0100\n", + " Status: active\n", + "\n", + "✓ Found customer: Sarah Johnson\n", + " Email: sarah.johnson@example.com\n", + " City: San Francisco\n", + " Phone: +1-415-555-0200\n", + " Status: active\n", + "✗ Customer 'Jane Doe' not found\n", + "\n", + "✓ Database connection closed\n" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/notebooks/multicloud-oracledb-at-aws/images/01.png b/notebooks/multicloud-oracledb-at-aws/images/01.png new file mode 100644 index 00000000..1d733b45 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-aws/images/01.png differ diff --git a/notebooks/multicloud-oracledb-at-aws/images/02.png b/notebooks/multicloud-oracledb-at-aws/images/02.png new file mode 100644 index 00000000..38844e4b Binary files /dev/null and b/notebooks/multicloud-oracledb-at-aws/images/02.png differ diff --git a/notebooks/multicloud-oracledb-at-aws/images/03.jpg b/notebooks/multicloud-oracledb-at-aws/images/03.jpg new file mode 100644 index 00000000..04b4c579 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-aws/images/03.jpg differ diff --git a/notebooks/multicloud-oracledb-at-aws/oracle-autonomous-db-at-aws.md b/notebooks/multicloud-oracledb-at-aws/oracle-autonomous-db-at-aws.md new file mode 100644 index 00000000..e448b841 --- /dev/null +++ b/notebooks/multicloud-oracledb-at-aws/oracle-autonomous-db-at-aws.md @@ -0,0 +1,750 @@ +# Creating Oracle Autonomous Database on AWS + +A comprehensive guide to provisioning Oracle Autonomous Database on Dedicated Exadata Infrastructure within AWS using Oracle Database@AWS. + +## Overview + +Oracle Database@AWS is a managed service that brings Oracle Exadata workloads to AWS, providing two key offerings: + +1. **Oracle Exadata Database Service on Dedicated Infrastructure** - Full control over Oracle Enterprise Edition with RAC capabilities +2. **Oracle Autonomous Database on Dedicated Exadata Infrastructure** - Fully managed database with AI/ML automation + +This guide focuses on setting up Oracle Autonomous Database on Dedicated Exadata Infrastructure. + +## Key Benefits + +- **Simplified Migration** - Migrate Oracle Exadata workloads with minimal changes +- **Single Invoice** - Unified billing through AWS Marketplace +- **AWS Integration** - Use AWS Console, CLI, and APIs for management +- **High Availability** - Exadata infrastructure with zero-ETL and S3 backups +- **Enterprise Features** - Full Oracle Enterprise Edition capabilities +- **Regional Flexibility** - Available in U.S. East (N. Virginia) and U.S. West (Oregon), expanding to 20+ regions globally + +## Prerequisites + +1. **AWS Account** - Active AWS account with appropriate permissions +2. **AWS Marketplace Access** - Access to Oracle Database@AWS offering +3. **Oracle License** - Bring Your Own License (BYOL) or license included options +4. **VPC Setup** - Existing VPC for EC2 application servers +5. **Permissions** - IAM permissions to manage ODB resources +6. **Sales Team Contact** - Coordinate with AWS/Oracle sales for account activation + +## Step 1: Request Access to Oracle Database@AWS + +### Via AWS Marketplace + +1. Sign in to [AWS Management Console](https://console.aws.amazon.com/) +2. Navigate to AWS Marketplace +3. Search for **Oracle Database@AWS** +4. Click on the offering +5. Select **Request a Private Offer** + +### Via AWS Console + +1. Go to [Oracle Database@AWS Console](https://console.aws.amazon.com/odb/home) +2. Click **Get Started** +3. Request a private offer through AWS Marketplace + +### Sales Team Engagement + +- Your AWS and Oracle sales teams will receive your request +- They will contact you to understand your workload requirements +- Discuss licensing options (BYOL vs. License Included) +- Activate your account for access +- Estimated timeframe: 1-2 business days + +## Step 2: Create an ODB Network + +An ODB Network is a private, isolated network that hosts OCI infrastructure on AWS, serving as the communication bridge between AWS and OCI. + +### Via AWS Console + +1. **Access ODB Dashboard** + - Open [Oracle Database@AWS Console](https://console.aws.amazon.com/odb/home) + - Select your region (US East N. Virginia or US West Oregon) + +2. **Create ODB Network** + - Click **Create ODB network** + - Provide the following details: + + **Basic Configuration:** + - **Network Name**: e.g., `prod-autonomous-network` + - **Availability Zone**: Select target AZ (e.g., us-east-1a) + + **Network CIDR Ranges:** + - **Client Connection CIDR**: e.g., `192.168.0.0/16` (for EC2 applications) + - **Backup Connection CIDR**: e.g., `192.169.0.0/16` (for S3 backups) + - Note: Each range must be a /24 or larger + + **Domain Configuration:** + - **Domain Name Prefix**: e.g., `mycompany` (optional) + - Final FQDN: `mycompany.oraclevcn.com` + +3. **Configure Advanced Options** (Optional) + - **S3 Backup Access**: Enable automated backups to Amazon S3 + - **Zero-ETL Integration**: Enable for Amazon Redshift analytics + - **VPC Endpoint Configuration**: For secure AWS service connectivity + +4. **Review and Create** + - Review all settings + - Click **Create** + - Creation typically takes 10-15 minutes + - Status visible in dashboard + +### Network Details Reference + +After creation, note these details for later configuration: + +``` +ODB Network Information: +├── Network ID: odb-xxxxxxx +├── Network Name: prod-autonomous-network +├── Availability Zone: us-east-1a +├── Client CIDR: 192.168.0.0/16 +├── Backup CIDR: 192.169.0.0/16 +├── Domain: mycompany.oraclevcn.com +└── Status: Available +``` + +## Step 3: Configure VPC Route Tables (For EC2 Access) + +Update your EC2 VPC route tables to enable connectivity to the ODB network. + +### Update EC2 VPC Route Tables + +1. **Get ODB Network Peering Information** + - From ODB Dashboard, go to **ODB Peering** + - Note the **Local Gateway Route Table ID** + - Copy the **ODB Network CIDR** (client connection range) + +2. **Access VPC Console** + - Open EC2 → VPC → Route Tables + - Find your application's VPC route table + +3. **Add Route to ODB Network** + - Click **Edit routes** + - Click **Add route** + - **Destination**: Enter ODB Network Client CIDR (e.g., 192.168.0.0/16) + - **Target**: Select **Local Gateway Route Table** (from ODB Peering) + - Click **Save routes** + +4. **Verify Connectivity** + ```bash + # From EC2 instance in the VPC + ping + + # Test DNS resolution + nslookup .oraclevcn.com + ``` + +## Step 4: Create Exadata Infrastructure + +The Exadata infrastructure is the underlying hardware architecture (database servers, storage servers, networking) that runs your autonomous databases. + +### Via AWS Console + +1. **Create Exadata Infrastructure** + - From ODB Dashboard, click **Create Exadata infrastructure** + - Provide the following details: + + **Basic Information:** + - **Infrastructure Name**: e.g., `prod-exadata-01` + - **Availability Zone**: Use same AZ as ODB Network + - **Display Name**: Human-readable name for reference + +2. **Select Exadata System Model** + - **System Model**: Select `Exadata.X11M` (latest generation) + - Other options may be available based on your region + +3. **Configure Database Servers** + - **Minimum**: 2 database servers + - **Maximum**: Up to 32 database servers + - Choose based on your workload CPU/memory needs + - Default configuration: 2 servers + +4. **Configure Storage Servers** + - **Minimum**: 3 storage servers + - **Maximum**: Up to 64 storage servers + - **Storage Capacity per Server**: 80 TB + - Default configuration: 3 servers (240 TB total) + +5. **Maintenance Preferences** + - **Patching Mode**: + - *Rolling* (default) - minimal downtime + - *Non-rolling* - single maintenance window + - **Maintenance Window**: Schedule preferred time + - **OCI Notification Contacts**: Email addresses for maintenance notifications + - **Timezone**: Set appropriate timezone + +6. **Review and Create** + - Review all infrastructure settings + - Click **Create Exadata infrastructure** + - Creation typically takes 45-60 minutes + - Status shown in dashboard + +### Exadata Infrastructure Sizing Guide + +| Workload Size | DB Servers | Storage Servers | Total Storage | Use Case | +|---------------|-----------|-----------------|---------------|----------| +| Small | 2 | 3 | 240 TB | Dev/Test, Small Production | +| Medium | 4 | 6 | 480 TB | Mid-size Production | +| Large | 8 | 12 | 960 TB | Enterprise Production | +| Extra Large | 16+ | 32+ | 2.5+ PB | Large Enterprise | + +**Note**: You cannot modify infrastructure after creation via AWS Console. For changes, use OCI Console. + +## Step 5: Create Autonomous VM Cluster + +An Autonomous VM Cluster is a fully managed set of virtual machines that automate key database management tasks using AI/ML. + +### Via AWS Console + +1. **Create Autonomous VM Cluster** + - From ODB Dashboard, click **Create Exadata VM cluster** + - Select **Autonomous VM Cluster** as the type + +2. **Basic Configuration** + - **VM Cluster Name**: e.g., `prod-autonomous-cluster-01` + - **Timezone**: Select database timezone (e.g., America/New_York) + - **Time Zone for Reports**: Same or different timezone for reporting + +3. **Licensing Options** + - **Bring Your Own License (BYOL)** + - Use existing Oracle licenses + - Best for existing Oracle customers + - Requires Oracle license count documentation + - **License Included** + - Oracle provides licenses + - Simpler procurement + - Higher per-unit cost + +4. **Infrastructure & Version Selection** + - **Exadata Infrastructure**: Select previously created infrastructure + - **Grid Infrastructure Version**: Select latest available (e.g., 21c) + - **Exadata Image Version**: Use recommended latest version + +5. **Database Server Configuration** + - **CPU Cores per Server**: Choose based on workload + - Small: 16-20 cores + - Medium: 28-32 cores + - Large: 40+ cores + - **Memory per Server**: Typically 2-4 GB per core + - **Local Storage**: SSD storage for database files and logs + - Click **Accept defaults** for standard sizing or **Customize** + +6. **Connectivity & Access** + - **Select ODB Network**: Choose your created ODB network + - **VM Cluster Prefix**: e.g., `autonomous-prod` + - Used for DNS naming + - Format: `.` (e.g., autonomous-prod.mycompany.oraclevcn.com) + + **Network Access Configuration:** + - **SCAN Listener Port**: Default `1521` or custom port (1024-8999) + - **SSH Key Pairs**: Paste public key(s) for SSH access + - Click **Add SSH Key** + - Paste public key content + - Add multiple keys if needed + +7. **Diagnostics & Monitoring** (Optional) + - **Enable Enterprise Manager**: For advanced monitoring + - **Enable Diagnostics**: CloudWatch metrics collection + - **CloudWatch Namespace**: `AWS/ODB` (default) + +8. **Tags** (Optional but recommended) + - **Environment**: e.g., `production` + - **Owner**: Team or department name + - **Application**: Application name + - **CostCenter**: For billing allocation + +9. **Review and Create** + - Review all VM cluster settings + - Verify licensing selection + - Confirm SSH key configuration + - Click **Create Autonomous VM cluster** + - **Creation time**: Up to 6 hours (depending on cluster size) + - Monitor progress in dashboard + +### VM Cluster Creation Status + +``` +Status Progression: +1. Creating (0-2 hours) - Infrastructure initialization +2. Available (2-6 hours) - Configuring database servers +3. Ready (6 hours) - Autonomous VM cluster operational +``` + +## Step 6: Create Oracle Autonomous Database + +Once the Autonomous VM Cluster is ready, create autonomous databases within it. + +### Via OCI Console + +**Note**: Autonomous databases are created in the OCI Console, not AWS Console. + +1. **Access OCI Console** + - From AWS ODB Dashboard, click **Manage in OCI** + - You'll be redirected to OCI Console + - Select your OCI compartment for the VM cluster + +2. **Create Autonomous Database** + - Go to **Oracle Database** → **Autonomous Database** + - Click **Create Autonomous Database** + - Select **Dedicated Infrastructure** + +3. **Provide Database Information** + - **Display Name**: e.g., `prod-autonomous-db` + - **Database Name**: e.g., `PRODADB` (alphanumeric, max 14 characters) + - **Workload Type**: Select **Autonomous Database on Dedicated Infrastructure** + +4. **Select VM Cluster** + - **Autonomous VM Cluster**: Select your created cluster + - **Container Database**: Will auto-create or use existing + +5. **Database Configuration** + - **Administrator Password**: Create strong password + - Minimum 12 characters + - Mix of uppercase, lowercase, numbers, special characters + - **Database Version**: Oracle 19c or 23ai + - **Character Set**: AL32UTF8 (recommended) or your choice + +6. **Advanced Options** + - **Auto Scaling**: Enable CPU/storage auto-scaling + - **Backup Retention**: 7-60 days (default 30) + - **Encryption**: Use Oracle managed keys or your key + - **Backup Location**: + - S3 bucket (for AWS backups) + - OCI Object Storage (default) + +7. **Backup Configuration** + - **Enable Backup**: Strongly recommended + - **Backup Destination**: S3 bucket (AWS) or Object Storage (OCI) + - **Automated Backups**: Daily backups configured automatically + +8. **Create Database** + - Review all settings + - Click **Create Autonomous Database** + - **Provisioning time**: 15-30 minutes + - Status visible in OCI Console + +### Database Sizing Recommendations + +| Workload | CPU Cores | Memory | Storage | PDB Users | +|----------|-----------|--------|---------|-----------| +| Development | 1-2 | 4-8 GB | 20-50 GB | 1-5 | +| Testing | 2-4 | 8-16 GB | 50-100 GB | 5-10 | +| Production | 4+ | 16+ GB | 100+ GB | 10+ | +| Enterprise | 8+ | 32+ GB | 500+ GB | 50+ | + +## Step 7: Connect to Autonomous Database + +After the database is created and ready, establish connections from your applications. + +### Get Connection Details + +1. **From OCI Console** + - Navigate to **Autonomous Database** → Your database + - Click **Database Connection** + - Download **Wallet** (for secure connections) + - Note the **Connection String** for your service + +2. **Available Services** + - `_high` - For batch operations + - `_medium` - For general use (recommended) + - `_low` - For lightweight connections + +### Connection Methods + +#### Option A: Using SQL Developer + +1. Download and install [Oracle SQL Developer](https://www.oracle.com/database/sqldeveloper/) +2. Create new database connection: + - Connection Name: `prod-autonomous-db` + - Username: `ADMIN` + - Password: Administrator password + - Connection Type: **Cloud Wallet** + - Configuration File: Download wallet from OCI Console + - Service: `_medium` +3. Test Connection → Connect + +#### Option B: Using SQL*Plus + +```bash +# Extract wallet and configure sqlnet.ora +unzip Wallet_.zip + +# Connect +sqlplus admin@_medium@/sqlnet.ora + +# Enter password +``` + +#### Option C: Using Python (cx_Oracle) + +```python +import cx_Oracle +import os + +# Download wallet from OCI Console +wallet_location = '/path/to/wallet' +os.environ['TNS_ADMIN'] = wallet_location + +try: + connection = cx_Oracle.connect( + user='admin', + password='', + dsn='_medium', + configdir=wallet_location + ) + print("✓ Successfully connected to Autonomous Database") + + cursor = connection.cursor() + cursor.execute('SELECT banner FROM v$version WHERE ROWNUM = 1') + print(cursor.fetchone()) + + cursor.close() + connection.close() +except cx_Oracle.DatabaseError as e: + print(f"✗ Connection failed: {e}") +``` + +#### Option D: Using JDBC (Java) + +```java +import java.sql.Connection; +import java.sql.DriverManager; + +public class AutonomousDBConnection { + public static void main(String[] args) { + String url = "jdbc:oracle:thin:@_medium?TNS_ADMIN=/path/to/wallet"; + String username = "admin"; + String password = ""; + + try { + Class.forName("oracle.jdbc.OracleDriver"); + Connection connection = DriverManager.getConnection(url, username, password); + System.out.println("✓ Connected to Autonomous Database"); + connection.close(); + } catch (Exception e) { + System.err.println("✗ Connection failed: " + e.getMessage()); + } + } +} +``` + +## Step 8: Configure Backups and Recovery + +Autonomous databases have automated backup strategies configured. + +### Backup Configuration + +1. **In OCI Console** + - Navigate to your Autonomous Database + - Go to **Backup** tab + - Review automatic backup schedule + - Create manual backups before major changes + - Set retention period (7-60 days) + +2. **Backup Locations** + - **OCI Object Storage** - Default backup location + - **Amazon S3** - For AWS-based backups + - Enable in backup settings + - Provide S3 bucket name and IAM role + +3. **Point-in-Time Recovery (PITR)** + - Enables recovery to any point within retention window + - Automatically enabled + - No additional configuration needed + +### Configure S3 Backups + +1. **Create S3 Bucket** + ```bash + aws s3 mb s3://oracle-autonomous-backups- + ``` + +2. **Create IAM Role for OCI** + - Trust relationship to OCI + - S3 bucket permissions + - Document role ARN + +3. **Enable S3 Backups** + - From Autonomous Database → Backup + - Click **Configure S3 Backups** + - Enter S3 bucket name + - Provide IAM role ARN + - Test connection + +## Step 9: Monitoring and Management + +### CloudWatch Monitoring + +Monitor your Autonomous Database using CloudWatch metrics. + +1. **Available Metrics** + - Go to **CloudWatch** → **Metrics** → **AWS/ODB** + - Available metric namespaces: + - VM Cluster metrics + - Container Database metrics + - Pluggable Database metrics + +2. **Key Metrics to Monitor** + - CPU Utilization + - Database Connections + - Storage Usage + - Network I/O + - Read/Write Latency + - Backup Status + +3. **Create Alarms** + ```bash + aws cloudwatch put-metric-alarm \ + --alarm-name autonomous-db-cpu-high \ + --alarm-description "Alert when CPU > 80%" \ + --metric-name CPUUtilization \ + --namespace AWS/ODB \ + --statistic Average \ + --period 300 \ + --threshold 80 \ + --comparison-operator GreaterThanThreshold + ``` + +### CloudTrail Logging + +All AWS API calls are logged in CloudTrail for audit purposes. + +1. **Enable CloudTrail** + - Already enabled by default for Oracle Database@AWS + - Logs available in CloudTrail console + +2. **Query Logs** + - View API calls made to Oracle Database@AWS + - Filter by resource, event type, or time range + - Export logs to S3 for analysis + +### EventBridge Integration + +Configure automated responses to database lifecycle events. + +1. **Create Event Rules** + ```bash + aws events put-rule \ + --name autonomous-db-backup-complete \ + --event-pattern '{"source":["aws.odb"],"detail-type":["Backup Complete"]}' + ``` + +2. **Configure Targets** + - SNS for notifications + - Lambda for automated actions + - SQS for event queuing + +## Step 10: Security Configuration + +### IAM Access Control + +1. **Create IAM Policies** + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "odb:DescribeAutonomousDatabase", + "odb:DescribeExadataInfrastructure", + "odb:CreateDatabase" + ], + "Resource": "arn:aws:odb:*:*:*" + } + ] + } + ``` + +2. **Assign to IAM Users/Roles** + - Grant principle of least privilege + - Separate read-only vs. admin access + - Use roles for EC2 instances + +### Network Security + +1. **VPC Configuration** + - ODB network is isolated private network + - EC2 access via peering only + - Configure security groups at VPC level + +2. **SSL/TLS Encryption** + - All connections encrypted by default + - Use wallet for certificate-based authentication + - Monitor certificate expiration + +### Encryption Options + +1. **Encryption at Rest** + - AWS managed keys (default) + - Customer managed keys (KMS) + - Oracle managed keys (in OCI) + +2. **Encryption in Transit** + - SSL/TLS for all network connections + - Wallet-based authentication + - No plaintext credentials in transit + +## Troubleshooting + +### Common Issues and Solutions + +#### Connection Failures + +**Issue**: "Cannot connect to database" +- Verify ODB network is in Available state +- Check VPC route tables include ODB network CIDR +- Confirm security groups allow traffic on port 1521 +- Verify DNS resolution: `nslookup .oraclevcn.com` + +**Issue**: "TNS listener does not know of service" +- Verify service name is correct (`_medium`, etc.) +- Confirm database is in Ready state +- Check wallet is current and not expired + +#### Backup Issues + +**Issue**: "Backup failed to S3" +- Verify S3 bucket exists and is accessible +- Check IAM role permissions for S3 bucket +- Confirm ODB network has S3 endpoint connectivity +- Review backup logs in CloudWatch + +#### Performance Issues + +**Issue**: "Slow query performance" +- Check CloudWatch CPU and I/O metrics +- Review database session count +- Consider auto-scaling enabled option +- Analyze execution plans in OCI Console +- Contact Oracle support for performance tuning + +### Support Resources + +- **AWS Support**: Through AWS Support Console +- **Oracle Support**: Through OCI Support channels +- **AWS Partners**: Oracle Competency Partners for implementation assistance +- **Documentation**: + - [AWS ODB User Guide](https://docs.aws.amazon.com/odb/) + - [OCI Documentation](https://docs.oracle.com/iaas/database-at-aws/) + +## Cost Estimation + +Oracle Database@AWS pricing includes: + +1. **Infrastructure Costs** + - Exadata infrastructure (per month) + - Database servers (CPU-based) + - Storage servers (capacity-based) + +2. **Software Costs** + - Oracle database licenses (if License Included) + - Support and updates + +3. **Additional AWS Costs** + - S3 storage for backups + - Data transfer (cross-region) + - CloudWatch monitoring + +**Estimate**: Use [Oracle Pricing Calculator](https://www.oracle.com/cloud/aws/pricing) for your specific configuration. + +## Best Practices + +1. **Infrastructure Planning** + - Size infrastructure for 80% peak load + - Plan for growth over next 3-5 years + - Account for dev/test environments + +2. **Backup Strategy** + - Maintain 30-day retention minimum + - Test restore procedures regularly + - Use S3 for geographic redundancy + +3. **Monitoring** + - Enable CloudWatch metrics + - Configure alarms for key thresholds + - Review logs regularly + +4. **Security** + - Rotate passwords regularly + - Keep wallet files secure + - Use IAM roles for application access + - Enable audit logging + +5. **Performance** + - Use appropriate service names for workload + - Enable auto-scaling for variable workloads + - Monitor and tune slow queries + - Use partitioning for large tables + +6. **Migration** + - Plan migration carefully + - Test with production-like datasets + - Use Oracle Data Pump for data migration + - Validate application compatibility + +## Next Steps + +After creating your Autonomous Database: + +1. **Load Data** + - Migrate existing data using Oracle Data Pump + - Or load sample datasets for testing + +2. **Configure Applications** + - Update connection strings + - Distribute wallet files securely + - Test application connectivity + +3. **Optimize Performance** + - Create indexes for frequently queried columns + - Partition large tables + - Monitor and tune slow queries + +4. **Implement Backups** + - Configure S3 backup destination + - Test restore procedures + - Document recovery runbooks + +5. **Enable Monitoring** + - Set up CloudWatch alarms + - Configure EventBridge notifications + - Review AWS CloudTrail logs regularly + +## Additional Resources + +- **AWS Documentation** + - [Oracle Database@AWS User Guide](https://docs.aws.amazon.com/odb/) + - [Onboarding Guide](https://docs.aws.amazon.com/odb/latest/UserGuide/setting-up.html) + - [How It Works](https://docs.aws.amazon.com/odb/latest/UserGuide/how-it-works.html) + +- **Oracle Documentation** + - [Database@AWS on OCI](https://docs.oracle.com/en/cloud/paas/autonomous-database/adbda/) + - [Autonomous Database Dedicated](https://docs.oracle.com/en/cloud/paas/autonomous-database/) + - [Provisioning Guide](https://docs.oracle.com/en/learn/exadb-provisioning-aws/) + +- **Support Channels** + - AWS Support through AWS Console + - OCI Support for Oracle-specific issues + - AWS Marketplace for billing questions + - Partner Network for implementation help + +## Summary + +Creating Oracle Autonomous Database on AWS involves: + +1. ✓ Requesting access and account activation +2. ✓ Creating an ODB network for private infrastructure +3. ✓ Configuring VPC routes for EC2 connectivity +4. ✓ Provisioning Exadata infrastructure (45-60 minutes) +5. ✓ Creating Autonomous VM cluster (up to 6 hours) +6. ✓ Deploying autonomous databases in OCI Console +7. ✓ Establishing secure connections from applications +8. ✓ Configuring backups and monitoring +9. ✓ Implementing security best practices +10. ✓ Monitoring and optimizing performance + +For semantic similarity search using embeddings with your autonomous database, refer to the [Oracle RAG Agents notebook](../rag-aiagent-chatbot/RAGChatbotwithAgentDevelopmentKit.ipynb). diff --git a/notebooks/multicloud-oracledb-at-aws/oracle-aws-similarity-search.ipynb b/notebooks/multicloud-oracledb-at-aws/oracle-aws-similarity-search.ipynb new file mode 100644 index 00000000..afb411a3 --- /dev/null +++ b/notebooks/multicloud-oracledb-at-aws/oracle-aws-similarity-search.ipynb @@ -0,0 +1,538 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "125af151", + "metadata": {}, + "source": [ + "# Oracle Database Similarity Search on AWS\n", + "\n", + "This notebook demonstrates semantic similarity search on data stored in Oracle Database hosted on AWS using AI embeddings.\n", + "\n", + "## Overview\n", + "- **Database**: Oracle Database on AWS (RDS or EC2)\n", + "- **Embedding Model**: sentence-transformers (all-MiniLM-L6-v2)\n", + "- **Similarity Metric**: Cosine similarity & Euclidean distance\n", + "- **Use Cases**: Document retrieval, semantic search, recommendations" + ] + }, + { + "cell_type": "markdown", + "id": "46509dc9", + "metadata": {}, + "source": [ + "## Section 1: Install & Import Required Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5073c755", + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "import sys\n", + "\n", + "packages = [\n", + " 'cx_Oracle>=8.0',\n", + " 'boto3',\n", + " 'pandas',\n", + " 'numpy',\n", + " 'scikit-learn',\n", + " 'sentence-transformers'\n", + "]\n", + "\n", + "for package in packages:\n", + " print(f\"Installing {package}...\")\n", + " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", package])\n", + "\n", + "print(\"✓ All packages installed successfully!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c24d3218", + "metadata": {}, + "outputs": [], + "source": [ + "import cx_Oracle\n", + "import boto3\n", + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn.metrics.pairwise import cosine_similarity\n", + "from sklearn.preprocessing import normalize\n", + "from sentence_transformers import SentenceTransformer\n", + "import warnings\n", + "import json\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "\n", + "print(\"✓ All libraries imported successfully!\")" + ] + }, + { + "cell_type": "markdown", + "id": "d3ec9d05", + "metadata": {}, + "source": [ + "## Section 2: Configure AWS Credentials and Database Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0664295f", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from google.colab import userdata\n", + "\n", + "# Load AWS credentials from Colab Secrets\n", + "try:\n", + " aws_access_key = userdata.get('AWS_ACCESS_KEY_ID')\n", + " aws_secret_key = userdata.get('AWS_SECRET_ACCESS_KEY')\n", + " print(\"✓ AWS credentials loaded from Colab Secrets\")\n", + "except:\n", + " print(\"⚠ AWS credentials not found in Colab Secrets\")\n", + " aws_access_key = None\n", + " aws_secret_key = None\n", + "\n", + "# Database configuration\n", + "DB_CONFIG = {\n", + " 'host': os.getenv('ORACLE_HOST', 'your-oracle-db-endpoint.rds.amazonaws.com'),\n", + " 'port': int(os.getenv('ORACLE_PORT', 1521)),\n", + " 'service_name': os.getenv('ORACLE_SERVICE', 'ORCL'),\n", + " 'user': os.getenv('ORACLE_USER', 'admin'),\n", + " 'password': os.getenv('ORACLE_PASSWORD', 'your_password_here')\n", + "}\n", + "\n", + "print(\"\\nDatabase Configuration:\")\n", + "print(f\" Host: {DB_CONFIG['host']}\")\n", + "print(f\" Port: {DB_CONFIG['port']}\")\n", + "print(f\" Service: {DB_CONFIG['service_name']}\")\n", + "print(f\" User: {DB_CONFIG['user']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "975ff435", + "metadata": {}, + "source": [ + "## Section 3: Connect to Oracle Database on AWS" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c4a2ebd", + "metadata": {}, + "outputs": [], + "source": [ + "def create_oracle_connection(config):\n", + " \"\"\"\n", + " Establish connection to Oracle Database.\n", + " \n", + " Args:\n", + " config (dict): Database configuration with host, port, service_name, user, password\n", + " \n", + " Returns:\n", + " cx_Oracle.Connection: Database connection object or None if failed\n", + " \"\"\"\n", + " try:\n", + " connection_string = (\n", + " f\"{config['user']}/{config['password']}@\"\n", + " f\"{config['host']}:{config['port']}/{config['service_name']}\"\n", + " )\n", + " connection = cx_Oracle.connect(connection_string)\n", + " print(f\"✓ Successfully connected to Oracle Database\")\n", + " print(f\" Version: {connection.version}\")\n", + " return connection\n", + " except cx_Oracle.DatabaseError as e:\n", + " error, = e.args\n", + " print(f\"✗ Connection Error: {error.message}\")\n", + " return None\n", + " except Exception as e:\n", + " print(f\"✗ Unexpected error: {str(e)}\")\n", + " return None\n", + "\n", + "# Establish connection\n", + "oracle_conn = create_oracle_connection(DB_CONFIG)" + ] + }, + { + "cell_type": "markdown", + "id": "4e2dafb9", + "metadata": {}, + "source": [ + "## Section 4: Load Data from Oracle Table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8f8b9ef", + "metadata": {}, + "outputs": [], + "source": [ + "def load_data_from_oracle(connection, table_name, columns=None):\n", + " \"\"\"\n", + " Load data from Oracle table into pandas DataFrame.\n", + " \n", + " Args:\n", + " connection: cx_Oracle connection object\n", + " table_name (str): Name of the table to load\n", + " columns (list): Specific columns to load (optional)\n", + " \n", + " Returns:\n", + " pd.DataFrame: Loaded data or None if failed\n", + " \"\"\"\n", + " try:\n", + " if columns:\n", + " column_str = \", \".join(columns)\n", + " query = f\"SELECT {column_str} FROM {table_name}\"\n", + " else:\n", + " query = f\"SELECT * FROM {table_name}\"\n", + " \n", + " df = pd.read_sql(query, connection)\n", + " print(f\"✓ Loaded {len(df)} records from {table_name}\")\n", + " print(f\" Columns: {list(df.columns)}\")\n", + " return df\n", + " except Exception as e:\n", + " print(f\"✗ Error loading data: {str(e)}\")\n", + " return None\n", + "\n", + "# Load sample data\n", + "if oracle_conn:\n", + " TABLE_NAME = 'DOCUMENTS'\n", + " COLUMNS = ['ID', 'TITLE', 'CONTENT', 'CATEGORY']\n", + " df = load_data_from_oracle(oracle_conn, TABLE_NAME, COLUMNS)\n", + " if df is not None:\n", + " print(\"\\nFirst 3 rows:\")\n", + " print(df.head(3))\n", + "else:\n", + " print(\"Cannot load data: Database connection not established\")\n", + " df = None" + ] + }, + { + "cell_type": "markdown", + "id": "09b7b8ec", + "metadata": {}, + "source": [ + "## Section 5: Prepare Data for Similarity Search" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78ae30f7", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Loading embedding model (first run may take a moment)...\")\n", + "embedding_model = SentenceTransformer('all-MiniLM-L6-v2')\n", + "print(\"✓ Embedding model loaded successfully\")\n", + "print(f\" Model: all-MiniLM-L6-v2\")\n", + "print(f\" Embedding dimension: 384\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e30b0b71", + "metadata": {}, + "outputs": [], + "source": [ + "def generate_embeddings(texts, model):\n", + " \"\"\"\n", + " Generate embeddings for a list of texts.\n", + " \n", + " Args:\n", + " texts (list): List of text strings\n", + " model: SentenceTransformer model\n", + " \n", + " Returns:\n", + " np.ndarray: Array of embeddings\n", + " \"\"\"\n", + " print(f\"Generating embeddings for {len(texts)} documents...\")\n", + " embeddings = model.encode(texts, show_progress_bar=True, batch_size=32)\n", + " print(f\"✓ Generated embeddings with shape: {embeddings.shape}\")\n", + " return embeddings\n", + "\n", + "# Prepare and embed data\n", + "if df is not None and not df.empty:\n", + " # Find text column\n", + " if 'CONTENT' in df.columns:\n", + " text_column = 'CONTENT'\n", + " elif 'TITLE' in df.columns:\n", + " text_column = 'TITLE'\n", + " else:\n", + " text_cols = df.select_dtypes(include=['object']).columns.tolist()\n", + " text_column = text_cols[0] if text_cols else None\n", + " \n", + " if text_column:\n", + " print(f\"Using text column: {text_column}\\n\")\n", + " df['text_clean'] = df[text_column].fillna('').astype(str).str.strip()\n", + " embeddings = generate_embeddings(df['text_clean'].tolist(), embedding_model)\n", + " df['embeddings'] = [embeddings[i] for i in range(len(df))]\n", + " print(\"✓ Data preparation complete\")\n", + " else:\n", + " print(\"No text column found in data\")\n", + "else:\n", + " print(\"No data available for embedding\")" + ] + }, + { + "cell_type": "markdown", + "id": "65409c42", + "metadata": {}, + "source": [ + "## Section 6: Implement Similarity Search Algorithm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d96a3647", + "metadata": {}, + "outputs": [], + "source": [ + "class OracleSimilaritySearch:\n", + " \"\"\"\n", + " Semantic similarity search engine for Oracle data.\n", + " Supports cosine similarity and euclidean distance metrics.\n", + " \"\"\"\n", + " \n", + " def __init__(self, dataframe, embedding_model):\n", + " \"\"\"\n", + " Initialize search engine.\n", + " \n", + " Args:\n", + " dataframe (pd.DataFrame): Data with embeddings column\n", + " embedding_model: SentenceTransformer model for encoding queries\n", + " \"\"\"\n", + " self.df = dataframe.copy()\n", + " self.model = embedding_model\n", + " self.embeddings = np.array([emb for emb in dataframe['embeddings'].values])\n", + " \n", + " def cosine_similarity_search(self, query, top_k=5):\n", + " \"\"\"\n", + " Search using cosine similarity metric.\n", + " \n", + " Args:\n", + " query (str): Search query\n", + " top_k (int): Number of top results to return\n", + " \n", + " Returns:\n", + " pd.DataFrame: Top k results with similarity scores\n", + " \"\"\"\n", + " # Encode query\n", + " query_embedding = self.model.encode([query])[0]\n", + " query_embedding = query_embedding.reshape(1, -1)\n", + " \n", + " # Compute similarities\n", + " similarities = cosine_similarity(query_embedding, self.embeddings)[0]\n", + " \n", + " # Get top k\n", + " top_indices = np.argsort(similarities)[::-1][:top_k]\n", + " results = self.df.iloc[top_indices].copy()\n", + " results['similarity_score'] = similarities[top_indices]\n", + " results['rank'] = range(1, len(results) + 1)\n", + " results['metric'] = 'cosine'\n", + " \n", + " # Clean up\n", + " if 'embeddings' in results.columns:\n", + " results = results.drop(columns=['embeddings', 'text_clean'])\n", + " \n", + " return results[['rank', 'similarity_score', 'metric'] + \n", + " [col for col in results.columns \n", + " if col not in ['rank', 'similarity_score', 'metric']]].reset_index(drop=True)\n", + " \n", + " def euclidean_distance_search(self, query, top_k=5):\n", + " \"\"\"\n", + " Search using euclidean distance metric.\n", + " \n", + " Args:\n", + " query (str): Search query\n", + " top_k (int): Number of top results to return\n", + " \n", + " Returns:\n", + " pd.DataFrame: Top k results with distance scores\n", + " \"\"\"\n", + " # Encode query\n", + " query_embedding = self.model.encode([query])[0]\n", + " \n", + " # Compute distances\n", + " distances = np.sqrt(np.sum((self.embeddings - query_embedding) ** 2, axis=1))\n", + " \n", + " # Get top k (lowest distances)\n", + " top_indices = np.argsort(distances)[:top_k]\n", + " results = self.df.iloc[top_indices].copy()\n", + " results['distance_score'] = distances[top_indices]\n", + " results['rank'] = range(1, len(results) + 1)\n", + " results['metric'] = 'euclidean'\n", + " \n", + " # Clean up\n", + " if 'embeddings' in results.columns:\n", + " results = results.drop(columns=['embeddings', 'text_clean'])\n", + " \n", + " return results[['rank', 'distance_score', 'metric'] + \n", + " [col for col in results.columns \n", + " if col not in ['rank', 'distance_score', 'metric']]].reset_index(drop=True)\n", + "\n", + "# Initialize search engine\n", + "if df is not None and 'embeddings' in df.columns:\n", + " search_engine = OracleSimilaritySearch(df, embedding_model)\n", + " print(\"✓ Similarity search engine initialized\")\n", + "else:\n", + " search_engine = None\n", + " print(\"Cannot initialize search engine: embeddings not available\")" + ] + }, + { + "cell_type": "markdown", + "id": "f1837f6d", + "metadata": {}, + "source": [ + "## Section 7: Execute Similarity Search Queries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b30da73f", + "metadata": {}, + "outputs": [], + "source": [ + "# Example cosine similarity queries\n", + "if search_engine is not None:\n", + " test_queries = [\n", + " \"machine learning and AI\",\n", + " \"cloud database solutions\",\n", + " \"data security and privacy\"\n", + " ]\n", + " \n", + " print(\"=\" * 80)\n", + " print(\"COSINE SIMILARITY SEARCH RESULTS\")\n", + " print(\"=\" * 80)\n", + " \n", + " for query in test_queries:\n", + " print(f\"\\nQuery: '{query}'\")\n", + " print(\"-\" * 80)\n", + " results = search_engine.cosine_similarity_search(query, top_k=3)\n", + " print(results[['rank', 'similarity_score']].to_string(index=False))\n", + "else:\n", + " print(\"Search engine not available\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5117fa8", + "metadata": {}, + "outputs": [], + "source": [ + "# Example euclidean distance queries\n", + "if search_engine is not None:\n", + " test_query = \"enterprise infrastructure\"\n", + " \n", + " print(\"\\n\" + \"=\" * 80)\n", + " print(\"EUCLIDEAN DISTANCE SEARCH RESULTS\")\n", + " print(\"=\" * 80)\n", + " print(f\"\\nQuery: '{test_query}'\")\n", + " print(\"-\" * 80)\n", + " results = search_engine.euclidean_distance_search(test_query, top_k=3)\n", + " print(results[['rank', 'distance_score']].to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "id": "adbfce77", + "metadata": {}, + "source": [ + "## Section 8: Display Results and Cleanup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b65d3f16", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# Visualization of search results\n", + "if search_engine is not None:\n", + " query = \"data analysis\"\n", + " results = search_engine.cosine_similarity_search(query, top_k=5)\n", + " \n", + " if len(results) > 0:\n", + " print(f\"Top Results for: '{query}'\")\n", + " print(\"=\" * 80)\n", + " print(results[['rank', 'similarity_score']].to_string(index=False))\n", + " \n", + " # Plot results\n", + " fig, ax = plt.subplots(figsize=(10, 6))\n", + " ax.barh(range(len(results)), results['similarity_score'], color='steelblue')\n", + " ax.set_yticks(range(len(results)))\n", + " ax.set_yticklabels([f\"Result {i+1}\" for i in range(len(results))])\n", + " ax.set_xlabel('Similarity Score', fontsize=12)\n", + " ax.set_ylabel('Results', fontsize=12)\n", + " ax.set_title(f'Similarity Search Results: \"{query}\"', fontsize=14, fontweight='bold')\n", + " ax.set_xlim([0, 1])\n", + " plt.tight_layout()\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7eca68fa", + "metadata": {}, + "outputs": [], + "source": [ + "# Close database connection\n", + "if oracle_conn:\n", + " try:\n", + " oracle_conn.close()\n", + " print(\"✓ Oracle database connection closed successfully\")\n", + " except Exception as e:\n", + " print(f\"⚠ Error closing connection: {str(e)}\")\n", + "\n", + "print(\"\\n\" + \"=\" * 80)\n", + "print(\"SUMMARY\")\n", + "print(\"=\" * 80)\n", + "print(\"\"\"\n", + "This notebook demonstrated:\n", + "1. Installing and importing required libraries (cx_Oracle, sentence-transformers, etc.)\n", + "2. Configuring AWS credentials and Oracle database parameters\n", + "3. Establishing secure connection to Oracle Database on AWS\n", + "4. Loading data from Oracle tables into pandas DataFrames\n", + "5. Generating vector embeddings using pre-trained transformer models\n", + "6. Implementing efficient similarity search algorithms\n", + "7. Executing semantic search queries with multiple similarity metrics\n", + "8. Visualizing and analyzing search results\n", + "\n", + "Best Practices:\n", + "- Always close database connections when done\n", + "- Cache embeddings to avoid regeneration\n", + "- Use batch processing for large datasets\n", + "- Monitor connection performance and timeouts\n", + "- Implement proper error handling for production use\n", + "\"\"\")" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/multicloud-oracledb-at-aws/oracle-db-at-aws-create.md b/notebooks/multicloud-oracledb-at-aws/oracle-db-at-aws-create.md new file mode 100644 index 00000000..a72aa5e9 --- /dev/null +++ b/notebooks/multicloud-oracledb-at-aws/oracle-db-at-aws-create.md @@ -0,0 +1,408 @@ +# Creating and Connecting to Oracle Database on AWS RDS + +This guide provides step-by-step instructions for creating an Oracle Database instance on Amazon RDS (Relational Database Service) and connecting to it from your applications. + +## Prerequisites + +Before you begin, ensure you have: + +1. **AWS Account** - An active AWS account with appropriate permissions +2. **AWS Management Console Access** - To navigate RDS dashboard +3. **AWS CLI (Optional)** - For command-line database creation +4. **Network Access** - Proper VPC and security group configuration +5. **Oracle Client Tools (Optional)** - For local connections (SQL*Plus, SQL Developer) + +## Step 1: Sign In to AWS Console + +1. Open the [AWS Management Console](https://aws.amazon.com/console/) +2. Sign in with your AWS account credentials +3. Select your desired region (e.g., us-east-1, us-west-2) +4. Search for and navigate to **RDS (Relational Database Service)** + +## Step 2: Create an RDS Oracle Database Instance + +### Using AWS Management Console + +1. **Open RDS Dashboard** + - Go to Services → RDS → Databases + - Click **Create database** + +2. **Choose Database Creation Method** + - Select **Standard create** (for more control) + - Or **Easy create** (for quick setup with defaults) + +3. **Select Engine** + - Engine type: **Oracle** + - Edition: Choose one of: + - Oracle Database 19c (recommended for latest features) + - Oracle Database 21c (latest version) + - Oracle Database 12c Release 2 (for legacy compatibility) + - License: Choose **License included** or **Bring your own license (BYOL)** + +4. **Database Instance Class** + - Instance class: Select based on workload: + - **db.t3.micro** - For testing/development (Free Tier eligible) + - **db.t3.small** - Light workloads + - **db.t3.medium** - Small production workloads + - **db.m5.large** - Production workloads + - Larger sizes for heavy production use + - Multi-AZ: Enable for production (High Availability) + - Storage type: **gp3** (General Purpose SSD) or **io1** (IOPS-optimized) + - Allocated storage: 100 GB minimum, 20+ GB recommended + +5. **Database Identifier and Credentials** + - DB instance identifier: `oracle-dev` (or your preferred name) + - Master username: `admin` (or custom username) + - Master password: Create a strong password + - Minimum 8 characters + - Mix of uppercase, lowercase, numbers, special characters + - **Save password in AWS Secrets Manager** (recommended) + +6. **Connectivity** + - VPC: Select your VPC (default or custom) + - DB subnet group: Create new or use existing + - Public accessibility: + - **No** - For internal applications only + - **Yes** - For external connections (requires security group rules) + - Availability Zone: Select preferred AZ or let AWS choose + - Security group: Create new or use existing + - Default security group is created automatically + +7. **Database Authentication** + - Authentication: **Password authentication** (standard) + - Optional: Enable IAM database authentication for additional security + +8. **Advanced Settings** (Optional but recommended) + - Database name: `ORCL` (or your custom name) + - Port: `1521` (default Oracle port) + - Parameter group: Default or custom + - Option group: Default or custom + - Backup retention: 7 days (adjust as needed) + - Backup window: Select preferred maintenance window + - Enable encryption: Yes (Recommended) + - KMS key: Use AWS managed key or custom key + - Enable Enhanced Monitoring: Yes (for production) + - Logs to publish: Alert log, audit log, trace file + +9. **Review and Create** + - Review all settings + - Click **Create database** + - Wait for database creation (typically 5-10 minutes) + +### Using AWS CLI + +```bash +aws rds create-db-instance \ + --db-instance-identifier oracle-dev \ + --db-instance-class db.t3.small \ + --engine oracle-ee \ + --engine-version 19.0.0.0 \ + --master-username admin \ + --master-user-password MySecurePassword123! \ + --allocated-storage 100 \ + --db-name ORCL \ + --port 1521 \ + --vpc-security-group-ids sg-xxxxxxxx \ + --publicly-accessible true \ + --backup-retention-period 7 +``` + +## Step 3: Configure Security Groups + +### For Internal Access Only + +1. Open EC2 → Security Groups +2. Find the RDS security group +3. **Inbound rules** - Add rule: + - Type: Custom TCP + - Port range: 1521 (Oracle default) + - Source: Security group of application EC2 instances + - Description: "Oracle RDS access from application" + +### For External Access + +1. Open EC2 → Security Groups +2. Find the RDS security group +3. **Inbound rules** - Add rule: + - Type: Custom TCP + - Port range: 1521 + - Source: Your IP address (0.0.0.0/0 for any - not recommended for production) + - Description: "Oracle RDS access from external" + +## Step 4: Retrieve Connection Details + +1. In RDS Dashboard, click your Oracle instance +2. **Endpoint & Port** section shows: + - **Endpoint**: `oracle-dev.xxxxxxxx.us-east-1.rds.amazonaws.com` + - **Port**: `1521` +3. Save these details for connection strings + +## Step 5: Connect to Oracle Database + +### Option A: Using SQL*Plus (Command Line) + +```bash +# Install Oracle Instant Client (if not already installed) +# For macOS with homebrew: +brew install oracle-instantclient + +# For Linux, download from Oracle website + +# Connect to database +sqlplus admin@oracle-dev.xxxxxxxx.us-east-1.rds.amazonaws.com:1521/ORCL + +# When prompted, enter your master password +``` + +### Option B: Using SQL Developer (GUI) + +1. Download [Oracle SQL Developer](https://www.oracle.com/database/sqldeveloper/technologies/download/) +2. Create new connection: + - Connection Name: `AWS-Oracle-Dev` + - Username: `admin` + - Password: Your master password + - Hostname: `oracle-dev.xxxxxxxx.us-east-1.rds.amazonaws.com` + - Port: `1521` + - Service name: `ORCL` +3. Test connection → Connect + +### Option C: Using Python (cx_Oracle) + +```python +import cx_Oracle + +# Database connection details +db_config = { + 'user': 'admin', + 'password': 'YourMasterPassword', + 'host': 'oracle-dev.xxxxxxxx.us-east-1.rds.amazonaws.com', + 'port': 1521, + 'service_name': 'ORCL' +} + +# Build connection string +connection_string = ( + f"{db_config['user']}/{db_config['password']}@" + f"{db_config['host']}:{db_config['port']}/" + f"{db_config['service_name']}" +) + +# Connect to database +try: + connection = cx_Oracle.connect(connection_string) + print("✓ Connected to Oracle Database successfully") + + # Test query + cursor = connection.cursor() + cursor.execute("SELECT * FROM v$version WHERE ROWNUM = 1") + print(cursor.fetchone()) + + cursor.close() + connection.close() +except cx_Oracle.DatabaseError as e: + print(f"✗ Connection failed: {e}") +``` + +### Option D: Using Node.js (oracledb) + +```javascript +const oracledb = require('oracledb'); + +async function connectDB() { + try { + const connection = await oracledb.getConnection({ + user: 'admin', + password: 'YourMasterPassword', + connectString: 'oracle-dev.xxxxxxxx.us-east-1.rds.amazonaws.com:1521/ORCL' + }); + + console.log("✓ Connected to Oracle Database successfully"); + + // Test query + const result = await connection.execute( + 'SELECT * FROM v$version WHERE ROWNUM = 1' + ); + console.log(result.rows); + + await connection.close(); + } catch (error) { + console.error("✗ Connection failed:", error); + } +} + +connectDB(); +``` + +## Step 6: Load Sample Data + +### Create Sample Table + +```sql +CREATE TABLE documents ( + id NUMBER PRIMARY KEY, + title VARCHAR2(255), + content CLOB, + category VARCHAR2(100), + created_date TIMESTAMP DEFAULT SYSDATE +); +``` + +### Insert Sample Data + +```sql +INSERT INTO documents (id, title, content, category) +VALUES (1, 'Machine Learning Basics', 'Content about ML...', 'AI'); + +INSERT INTO documents (id, title, content, category) +VALUES (2, 'Cloud Database Solutions', 'Content about cloud...', 'Cloud'); + +COMMIT; +``` + +## Step 7: Enable AWS Secrets Manager Integration (Optional) + +### Store Database Credentials Securely + +1. Go to **AWS Secrets Manager** +2. Click **Store a new secret** +3. Secret type: **Credentials for RDS database** +4. Select your RDS instance +5. Username: `admin` +6. Password: Your master password +7. Secret name: `oracle-dev-credentials` +8. Click **Store** + +### Access from Application Code + +```python +import json +import boto3 + +secrets_client = boto3.client('secretsmanager') + +secret_response = secrets_client.get_secret_value( + SecretId='oracle-dev-credentials' +) + +secret = json.loads(secret_response['SecretString']) + +# Use in connection +db_config = { + 'user': secret['username'], + 'password': secret['password'], + 'host': secret['host'], + 'port': secret['port'], + 'service_name': secret['dbname'] +} +``` + +## Step 8: Monitor and Manage + +### CloudWatch Monitoring + +1. Open RDS Dashboard +2. Select your database instance +3. View **Monitoring** tab: + - CPU Utilization + - Database Connections + - Storage Space + - Network I/O + - Read/Write Latency + +### Backup and Restore + +1. Click your database instance +2. **Maintenance & backups**: + - Automated backups are enabled by default + - Create manual snapshots before major changes + - Define backup retention period + +### Modify Instance + +1. Click your database instance +2. Click **Modify**: + - Change instance class for performance + - Increase storage + - Enable Multi-AZ + - Change backup retention + - Update security settings +3. Click **Continue** and **Apply immediately** or schedule change + +## Troubleshooting + +### Connection Issues + +**Error: "Network error – could not resolve service name"** +- Verify RDS endpoint is correct +- Check security group allows port 1521 +- Ensure application is in same VPC or has network connectivity + +**Error: "ORA-12514: TNS:listener does not currently know of service requested"** +- Verify database name (ORCL) is correct +- Check RDS instance is in Available state +- Wait for RDS to finish initialization + +**Error: "ORA-01017: invalid username/password"** +- Verify master username and password +- Check for special characters in password +- Reset password from RDS console if needed + +### Performance Issues + +- Check CloudWatch metrics for CPU and I/O +- Review slow query logs +- Consider upgrading instance class +- Enable Enhanced Monitoring for detailed metrics + +## Best Practices + +1. **Security** + - Use strong passwords (minimum 8 characters, mixed case) + - Enable encryption at rest and in transit + - Use Security Groups to restrict access + - Consider IAM database authentication + - Store credentials in AWS Secrets Manager + +2. **Backup & Recovery** + - Enable automated backups with sufficient retention + - Create snapshots before major changes + - Test restore procedures regularly + - Use Multi-AZ for production environments + +3. **Performance** + - Choose appropriate instance class for workload + - Use gp3 storage for general purpose workloads + - Monitor CloudWatch metrics regularly + - Enable Enhanced Monitoring for production + +4. **Cost Optimization** + - Use db.t3 instances for variable workloads + - Disable unused features + - Use Reserved Instances for predictable workloads + - Set appropriate backup retention periods + +5. **Maintenance** + - Schedule maintenance windows during off-peak hours + - Keep database software up to date + - Monitor for deprecated features + - Review AWS notifications for database updates + +## Additional Resources + +- [AWS RDS Oracle Documentation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Oracle.html) +- [RDS Oracle Pricing](https://aws.amazon.com/rds/oracle/pricing/) +- [Oracle Database on AWS FAQs](https://aws.amazon.com/rds/oracle/faqs/) +- [RDS Best Practices](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_BestPractices.html) + +## Next Steps + +Once your Oracle database is running on AWS RDS: + +1. **Load Data** - Migrate existing data or load sample datasets +2. **Configure Networking** - Set up VPC endpoints for secure access +3. **Enable Monitoring** - Set up CloudWatch alarms for key metrics +4. **Implement Backups** - Configure automated backup strategy +5. **Create Indexes** - Optimize query performance for your workload +6. **Use with AI Tools** - Integrate with the Oracle RAG agents notebook for semantic search + +For more information on semantic similarity search using embeddings, see the [Oracle RAG Agents notebook](../rag-aiagent-chatbot/RAGChatbotwithAgentDevelopmentKit.ipynb). diff --git a/notebooks/multicloud-oracledb-at-aws/oracle-db-aws-bedrock.ipynb b/notebooks/multicloud-oracledb-at-aws/oracle-db-aws-bedrock.ipynb new file mode 100644 index 00000000..6350ab07 --- /dev/null +++ b/notebooks/multicloud-oracledb-at-aws/oracle-db-aws-bedrock.ipynb @@ -0,0 +1,933 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6016b7e2", + "metadata": {}, + "source": [ + "# Oracle Database with AWS Bedrock for RAG Search\n", + "\n", + "This notebook demonstrates how to integrate Oracle Database with Amazon Bedrock to build a powerful Retrieval-Augmented Generation (RAG) application. We'll leverage Oracle's vector storage capabilities with AWS Bedrock's foundation models for intelligent, context-aware search.\n", + "\n", + "## Architecture Overview\n", + "- **Oracle AI Database 26ai**: Vector database for storing and searching embeddings\n", + "- **Amazon Bedrock**: Serverless foundation models for embeddings and LLM inference\n", + "- **RAG Pattern**: Ground LLM responses with relevant context from your Oracle database\n", + "\n", + "> **Note**: For Google Colab, you'll need to set up AWS credentials securely and ensure connectivity to your Oracle Database@AWS instance." + ] + }, + { + "cell_type": "markdown", + "id": "0fcbf6fe", + "metadata": {}, + "source": [ + "## 1. Installation & Dependencies\n", + "\n", + "Install required Python packages for AWS Bedrock, Oracle Database, and LangChain integration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ca11927", + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "import sys\n", + "\n", + "def install_packages():\n", + " \"\"\"Install required packages\"\"\"\n", + " packages = [\n", + " 'python-oracledb',\n", + " 'boto3',\n", + " 'langchain',\n", + " 'langchain-aws',\n", + " 'langchain-text-splitters',\n", + " ]\n", + " \n", + " for package in packages:\n", + " print(f\"Installing {package}...\")\n", + " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", package])\n", + " \n", + " print(\"✓ All packages installed successfully!\")\n", + "\n", + "install_packages()" + ] + }, + { + "cell_type": "markdown", + "id": "9e13497e", + "metadata": {}, + "source": [ + "## 2. Import Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62bd0a96", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import boto3\n", + "import oracledb\n", + "import time\n", + "from typing import List, Dict\n", + "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", + "import getpass\n", + "\n", + "print(\"✓ Libraries imported successfully!\")" + ] + }, + { + "cell_type": "markdown", + "id": "b0d2d819", + "metadata": {}, + "source": [ + "## 3. Configuration\n", + "\n", + "Configure AWS and Oracle Database credentials. For Google Colab, store credentials securely using environment variables or Google Colab secrets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91a04fb9", + "metadata": {}, + "outputs": [], + "source": [ + "# Configuration Parameters\n", + "CONFIG = {\n", + " # AWS Configuration\n", + " 'AWS_REGION': 'us-east-1',\n", + " 'BEDROCK_EMBEDDINGS_MODEL': 'amazon.titan-embed-text-v2:0',\n", + " 'BEDROCK_LLM_MODEL': 'anthropic.claude-3-5-sonnet-20241022',\n", + " \n", + " # Oracle Configuration\n", + " 'ORACLE_USER': 'admin',\n", + " 'ORACLE_PASSWORD': '', # Will be prompted\n", + " 'ORACLE_HOST': '', # Will be prompted\n", + " 'ORACLE_PORT': 1522,\n", + " 'ORACLE_SERVICE': 'FREEPDB1',\n", + " \n", + " # RAG Configuration\n", + " 'CHUNK_SIZE': 1000,\n", + " 'CHUNK_OVERLAP': 200,\n", + " 'TOP_K': 3,\n", + " 'EMBEDDING_DIMENSION': 1536,\n", + "}\n", + "\n", + "# For Google Colab, prompt for sensitive information\n", + "print(\"AWS and Oracle Configuration Setup\")\n", + "print(\"=\" * 50)\n", + "\n", + "# AWS credentials should be set via environment variables or Colab secrets\n", + "# CONFIG['AWS_ACCESS_KEY_ID'] = getpass.getpass(\"Enter AWS Access Key ID: \")\n", + "# CONFIG['AWS_SECRET_ACCESS_KEY'] = getpass.getpass(\"Enter AWS Secret Access Key: \")\n", + "\n", + "# Oracle Database credentials\n", + "CONFIG['ORACLE_HOST'] = input(\"Enter Oracle Database Host: \")\n", + "CONFIG['ORACLE_PASSWORD'] = getpass.getpass(\"Enter Oracle Database Password: \")\n", + "\n", + "print(\"✓ Configuration loaded!\")" + ] + }, + { + "cell_type": "markdown", + "id": "ad33eaf1", + "metadata": {}, + "source": [ + "## 4. Initialize AWS and Oracle Connections" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bada5fa6", + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize Bedrock client\n", + "bedrock_client = boto3.client(\n", + " 'bedrock-runtime',\n", + " region_name=CONFIG['AWS_REGION']\n", + ")\n", + "\n", + "# Test Bedrock connectivity\n", + "try:\n", + " bedrock_client.list_foundation_models()\n", + " print(\"✓ Bedrock connection successful!\")\n", + "except Exception as e:\n", + " print(f\"✗ Bedrock connection failed: {e}\")\n", + " print(\"Make sure AWS credentials are set up properly via environment variables or boto3 configuration\")\n", + "\n", + "# Initialize Oracle connection\n", + "try:\n", + " oracle_connection = oracledb.connect(\n", + " user=CONFIG['ORACLE_USER'],\n", + " password=CONFIG['ORACLE_PASSWORD'],\n", + " dsn=f\"{CONFIG['ORACLE_HOST']}:{CONFIG['ORACLE_PORT']}/{CONFIG['ORACLE_SERVICE']}\"\n", + " )\n", + " print(\"✓ Oracle Database connection successful!\")\n", + "except Exception as e:\n", + " print(f\"✗ Oracle connection failed: {e}\")\n", + " print(\"Please check your database credentials and network connectivity\")" + ] + }, + { + "cell_type": "markdown", + "id": "48fee4a9", + "metadata": {}, + "source": [ + "## 5. Create Database Tables\n", + "\n", + "Create tables for vectors and RAG session history in Oracle." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ecb8fad", + "metadata": {}, + "outputs": [], + "source": [ + "def create_tables():\n", + " \"\"\"Create necessary tables for RAG application\"\"\"\n", + " cursor = oracle_connection.cursor()\n", + " \n", + " # Create documents table\n", + " create_docs_table = \"\"\"\n", + " CREATE TABLE IF NOT EXISTS documents (\n", + " doc_id VARCHAR2(256) PRIMARY KEY,\n", + " content CLOB,\n", + " embedding VECTOR(1536),\n", + " metadata JSON,\n", + " created_at TIMESTAMP DEFAULT SYSTIMESTAMP\n", + " )\n", + " \"\"\"\n", + " \n", + " # Create vector index\n", + " create_index = \"\"\"\n", + " CREATE INDEX IF NOT EXISTS doc_vector_idx ON documents(embedding) \n", + " INDEXTYPE IS VECTOR_INDEX\n", + " \"\"\"\n", + " \n", + " # Create RAG sessions table\n", + " create_sessions_table = \"\"\"\n", + " CREATE TABLE IF NOT EXISTS rag_sessions (\n", + " session_id VARCHAR2(256) PRIMARY KEY,\n", + " query VARCHAR2(4000),\n", + " response CLOB,\n", + " context CLOB,\n", + " model_id VARCHAR2(256),\n", + " created_at TIMESTAMP DEFAULT SYSTIMESTAMP\n", + " )\n", + " \"\"\"\n", + " \n", + " try:\n", + " cursor.execute(\"DROP TABLE documents\")\n", + " oracle_connection.commit()\n", + " except:\n", + " pass\n", + " \n", + " try:\n", + " cursor.execute(\"DROP TABLE rag_sessions\")\n", + " oracle_connection.commit()\n", + " except:\n", + " pass\n", + " \n", + " # Create tables\n", + " cursor.execute(create_docs_table)\n", + " oracle_connection.commit()\n", + " print(\"✓ documents table created\")\n", + " \n", + " cursor.execute(create_sessions_table)\n", + " oracle_connection.commit()\n", + " print(\"✓ rag_sessions table created\")\n", + " \n", + " cursor.close()\n", + "\n", + "# Create tables\n", + "create_tables()\n", + "print(\"✓ Database tables initialized!\")" + ] + }, + { + "cell_type": "markdown", + "id": "44ce195b", + "metadata": {}, + "source": [ + "## 6. Embedding Generation\n", + "\n", + "Create embeddings using Amazon Bedrock's Titan Embeddings model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "048b1357", + "metadata": {}, + "outputs": [], + "source": [ + "def generate_embedding(text: str) -> List[float]:\n", + " \"\"\"\n", + " Generate embedding using Amazon Titan Embeddings model\n", + " \n", + " Args:\n", + " text: Input text to embed\n", + " \n", + " Returns:\n", + " List of embedding values\n", + " \"\"\"\n", + " response = bedrock_client.invoke_model(\n", + " modelId=CONFIG['BEDROCK_EMBEDDINGS_MODEL'],\n", + " contentType='application/json',\n", + " accept='application/json',\n", + " body=json.dumps({\n", + " \"inputText\": text,\n", + " \"dimensions\": CONFIG['EMBEDDING_DIMENSION'],\n", + " \"normalize\": True\n", + " })\n", + " )\n", + " \n", + " embedding = json.loads(response['body'].read())['embedding']\n", + " return embedding\n", + "\n", + "# Test embedding generation\n", + "test_text = \"Oracle Database is integrated with AWS Bedrock for RAG search\"\n", + "test_embedding = generate_embedding(test_text)\n", + "print(f\"✓ Generated embedding with {len(test_embedding)} dimensions\")\n", + "print(f\" Sample values: {test_embedding[:5]}\")" + ] + }, + { + "cell_type": "markdown", + "id": "b24aa38d", + "metadata": {}, + "source": [ + "## 7. Document Ingestion\n", + "\n", + "Load documents, split into chunks, and store embeddings in Oracle." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05767703", + "metadata": {}, + "outputs": [], + "source": [ + "def ingest_documents(text: str, source: str = \"sample\"):\n", + " \"\"\"\n", + " Ingest documents into Oracle vector store\n", + " \n", + " Args:\n", + " text: Document content to ingest\n", + " source: Source identifier for the documents\n", + " \"\"\"\n", + " # Split into chunks\n", + " splitter = RecursiveCharacterTextSplitter(\n", + " chunk_size=CONFIG['CHUNK_SIZE'],\n", + " chunk_overlap=CONFIG['CHUNK_OVERLAP']\n", + " )\n", + " chunks = splitter.split_text(text)\n", + " \n", + " # Generate embeddings and store\n", + " cursor = oracle_connection.cursor()\n", + " \n", + " for i, chunk in enumerate(chunks):\n", + " print(f\"Processing chunk {i+1}/{len(chunks)}...\", end='\\r')\n", + " \n", + " embedding = generate_embedding(chunk)\n", + " doc_id = f\"{source}_chunk_{i}_{int(time.time())}\"\n", + " \n", + " cursor.execute(\n", + " \"\"\"\n", + " INSERT INTO documents (doc_id, content, embedding, metadata)\n", + " VALUES (:1, :2, :3, :4)\n", + " \"\"\",\n", + " [\n", + " doc_id,\n", + " chunk,\n", + " embedding,\n", + " json.dumps({\"chunk_index\": i, \"source\": source})\n", + " ]\n", + " )\n", + " \n", + " oracle_connection.commit()\n", + " cursor.close()\n", + " \n", + " print(f\"\\n✓ Ingested {len(chunks)} document chunks from {source}\")\n", + " return len(chunks)\n", + "\n", + "# Example: Ingest sample documents\n", + "sample_documents = \"\"\"\n", + "Oracle AI Database 26ai is the newest version of Oracle's enterprise database with built-in AI capabilities.\n", + "It provides native vector search functionality for machine learning applications.\n", + "Integration with AWS Bedrock allows seamless connection to foundation models.\n", + "Amazon Bedrock provides access to foundation models through a unified API.\n", + "You can use Bedrock for embeddings generation and language model inference.\n", + "RAG (Retrieval-Augmented Generation) improves LLM responses by grounding them with relevant context.\n", + "Oracle Vector Index enables efficient similarity search for vector embeddings.\n", + "The combination of Oracle and Bedrock creates a powerful knowledge base system.\n", + "\"\"\"\n", + "\n", + "chunks_ingested = ingest_documents(sample_documents, source=\"tutorial\")" + ] + }, + { + "cell_type": "markdown", + "id": "55923a6b", + "metadata": {}, + "source": [ + "## 8. Retrieval: Vector Similarity Search\n", + "\n", + "Retrieve relevant documents from Oracle using vector similarity search." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43342865", + "metadata": {}, + "outputs": [], + "source": [ + "def retrieve_context(query: str, top_k: int = None) -> List[Dict]:\n", + " \"\"\"\n", + " Retrieve relevant documents from Oracle vector store\n", + " \n", + " Args:\n", + " query: User query string\n", + " top_k: Number of top results to return\n", + " \n", + " Returns:\n", + " List of relevant documents with similarity scores\n", + " \"\"\"\n", + " if top_k is None:\n", + " top_k = CONFIG['TOP_K']\n", + " \n", + " # Generate query embedding\n", + " query_embedding = generate_embedding(query)\n", + " \n", + " # Perform similarity search in Oracle\n", + " cursor = oracle_connection.cursor()\n", + " cursor.execute(\n", + " \"\"\"\n", + " SELECT doc_id, content, VECTOR_DISTANCE(embedding, :1) AS distance\n", + " FROM documents\n", + " ORDER BY distance\n", + " FETCH FIRST :2 ROWS ONLY\n", + " \"\"\",\n", + " [query_embedding, top_k]\n", + " )\n", + " \n", + " results = cursor.fetchall()\n", + " cursor.close()\n", + " \n", + " context = [\n", + " {\n", + " \"id\": r[0],\n", + " \"content\": r[1],\n", + " \"score\": float(r[2])\n", + " }\n", + " for r in results\n", + " ]\n", + " \n", + " return context\n", + "\n", + "# Test retrieval\n", + "test_query = \"How to integrate Oracle with AWS Bedrock for RAG?\"\n", + "retrieved_docs = retrieve_context(test_query)\n", + "\n", + "print(f\"Query: {test_query}\")\n", + "print(f\"\\nRetrieved {len(retrieved_docs)} documents:\\n\")\n", + "for i, doc in enumerate(retrieved_docs, 1):\n", + " print(f\"Document {i}: {doc['id']}\")\n", + " print(f\"Similarity Score: {doc['score']:.4f}\")\n", + " print(f\"Content: {doc['content'][:150]}...\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "f80f6aca", + "metadata": {}, + "source": [ + "## 9. LLM Response Generation\n", + "\n", + "Generate responses using Bedrock's Claude model, grounded with retrieved context." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "667f2d85", + "metadata": {}, + "outputs": [], + "source": [ + "def generate_rag_response(query: str, context_docs: List[Dict]) -> str:\n", + " \"\"\"\n", + " Generate response using Bedrock LLM with context\n", + " \n", + " Args:\n", + " query: User query\n", + " context_docs: List of context documents from retrieval\n", + " \n", + " Returns:\n", + " Generated response from the LLM\n", + " \"\"\"\n", + " # Prepare context string\n", + " context_str = \"\\n\\n\".join(\n", + " [f\"[{doc['id']}]\\n{doc['content']}\" for doc in context_docs]\n", + " )\n", + " \n", + " # Prepare system prompt\n", + " system_prompt = \"\"\"You are a helpful assistant that answers questions based on the provided context. \n", + "Always reference the source document when applicable.\n", + "If the context doesn't contain relevant information, say so clearly.\"\"\"\n", + " \n", + " user_message = f\"\"\"Context:\n", + "{context_str}\n", + "\n", + "Question: {query}\n", + "\n", + "Please provide a comprehensive answer based on the context above.\"\"\"\n", + " \n", + " # Invoke Bedrock LLM\n", + " response = bedrock_client.invoke_model(\n", + " modelId=CONFIG['BEDROCK_LLM_MODEL'],\n", + " contentType='application/json',\n", + " accept='application/json',\n", + " body=json.dumps({\n", + " \"anthropic_version\": \"bedrock-2023-06-01\",\n", + " \"max_tokens\": 1024,\n", + " \"system\": system_prompt,\n", + " \"messages\": [\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": user_message\n", + " }\n", + " ]\n", + " })\n", + " )\n", + " \n", + " response_text = json.loads(response['body'].read())['content'][0]['text']\n", + " return response_text\n", + "\n", + "# Test LLM response generation\n", + "test_query = \"What are the key benefits of Oracle AI Database?\"\n", + "test_context = retrieve_context(test_query)\n", + "\n", + "if test_context:\n", + " response = generate_rag_response(test_query, test_context)\n", + " print(f\"Query: {test_query}\\n\")\n", + " print(f\"Response:\\n{response}\")\n", + "else:\n", + " print(\"No context retrieved for the query\")" + ] + }, + { + "cell_type": "markdown", + "id": "f719a487", + "metadata": {}, + "source": [ + "## 10. Complete RAG Pipeline\n", + "\n", + "Integrate all components into a complete RAG pipeline with session tracking." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4cd7d4b2", + "metadata": {}, + "outputs": [], + "source": [ + "def rag_pipeline(query: str, top_k: int = None) -> Dict:\n", + " \"\"\"\n", + " Complete RAG pipeline: retrieve context and generate response\n", + " \n", + " Args:\n", + " query: User query\n", + " top_k: Number of top documents to retrieve\n", + " \n", + " Returns:\n", + " Dictionary with query, response, context, and session info\n", + " \"\"\"\n", + " if top_k is None:\n", + " top_k = CONFIG['TOP_K']\n", + " \n", + " # Retrieve context\n", + " context_docs = retrieve_context(query, top_k)\n", + " \n", + " if not context_docs:\n", + " return {\n", + " \"query\": query,\n", + " \"response\": \"No relevant context found in the knowledge base.\",\n", + " \"context\": [],\n", + " \"session_id\": None\n", + " }\n", + " \n", + " # Generate response\n", + " response = generate_rag_response(query, context_docs)\n", + " \n", + " # Store in session history\n", + " session_id = f\"session_{int(time.time())}\"\n", + " cursor = oracle_connection.cursor()\n", + " \n", + " cursor.execute(\n", + " \"\"\"\n", + " INSERT INTO rag_sessions \n", + " (session_id, query, response, context, model_id)\n", + " VALUES (:1, :2, :3, :4, :5)\n", + " \"\"\",\n", + " [\n", + " session_id,\n", + " query,\n", + " response,\n", + " json.dumps([{\"id\": d[\"id\"], \"score\": d[\"score\"]} for d in context_docs]),\n", + " CONFIG['BEDROCK_LLM_MODEL']\n", + " ]\n", + " )\n", + " oracle_connection.commit()\n", + " cursor.close()\n", + " \n", + " return {\n", + " \"session_id\": session_id,\n", + " \"query\": query,\n", + " \"response\": response,\n", + " \"context\": context_docs\n", + " }\n", + "\n", + "# Test the complete RAG pipeline\n", + "print(\"=\" * 60)\n", + "print(\"Testing Complete RAG Pipeline\")\n", + "print(\"=\" * 60)\n", + "\n", + "test_queries = [\n", + " \"What is Oracle AI Database?\",\n", + " \"How does Bedrock help with AI applications?\",\n", + " \"What is RAG and why is it useful?\"\n", + "]\n", + "\n", + "results = []\n", + "for query in test_queries:\n", + " print(f\"\\nQuery: {query}\")\n", + " result = rag_pipeline(query)\n", + " results.append(result)\n", + " print(f\"Response: {result['response'][:200]}...\")\n", + " print(f\"Session ID: {result['session_id']}\")\n", + "\n", + "print(\"\\n✓ RAG Pipeline tests completed!\")" + ] + }, + { + "cell_type": "markdown", + "id": "f3ffcb59", + "metadata": {}, + "source": [ + "## 11. Utility Functions\n", + "\n", + "Helper functions for interactive queries, diagnostics, and cleanup." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba544511", + "metadata": {}, + "outputs": [], + "source": [ + "def get_database_stats() -> Dict:\n", + " \"\"\"Get statistics about stored documents\"\"\"\n", + " cursor = oracle_connection.cursor()\n", + " \n", + " cursor.execute(\"SELECT COUNT(*) FROM documents\")\n", + " doc_count = cursor.fetchone()[0]\n", + " \n", + " cursor.execute(\"SELECT COUNT(*) FROM rag_sessions\")\n", + " session_count = cursor.fetchone()[0]\n", + " \n", + " cursor.close()\n", + " \n", + " return {\n", + " \"total_documents\": doc_count,\n", + " \"total_sessions\": session_count\n", + " }\n", + "\n", + "def display_session_history(limit: int = 5):\n", + " \"\"\"Display recent RAG sessions\"\"\"\n", + " cursor = oracle_connection.cursor()\n", + " \n", + " cursor.execute(\n", + " \"\"\"\n", + " SELECT session_id, query, created_at \n", + " FROM rag_sessions \n", + " ORDER BY created_at DESC \n", + " FETCH FIRST :1 ROWS ONLY\n", + " \"\"\",\n", + " [limit]\n", + " )\n", + " \n", + " print(f\"Recent Sessions (last {limit}):\\n\")\n", + " for row in cursor.fetchall():\n", + " print(f\"Session: {row[0]}\")\n", + " print(f\"Query: {row[1]}\")\n", + " print(f\"Time: {row[2]}\\n\")\n", + " \n", + " cursor.close()\n", + "\n", + "def check_connectivity():\n", + " \"\"\"Verify connections to AWS and Oracle\"\"\"\n", + " print(\"Connection Status:\")\n", + " print(\"-\" * 40)\n", + " \n", + " # Check Bedrock\n", + " try:\n", + " bedrock_client.list_foundation_models()\n", + " print(\"✓ Bedrock: Connected\")\n", + " except Exception as e:\n", + " print(f\"✗ Bedrock: Failed - {e}\")\n", + " \n", + " # Check Oracle\n", + " try:\n", + " cursor = oracle_connection.cursor()\n", + " cursor.execute(\"SELECT 1 FROM DUAL\")\n", + " print(\"✓ Oracle: Connected\")\n", + " cursor.close()\n", + " except Exception as e:\n", + " print(f\"✗ Oracle: Failed - {e}\")\n", + "\n", + "# Display initial stats\n", + "stats = get_database_stats()\n", + "print(\"Database Statistics:\")\n", + "print(f\" Documents: {stats['total_documents']}\")\n", + "print(f\" Sessions: {stats['total_sessions']}\")\n", + "print()\n", + "\n", + "# Check connectivity\n", + "check_connectivity()" + ] + }, + { + "cell_type": "markdown", + "id": "e3085eb1", + "metadata": {}, + "source": [ + "## 12. Interactive RAG Query\n", + "\n", + "Ask custom questions and get RAG-powered responses." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "420506af", + "metadata": {}, + "outputs": [], + "source": [ + "# Example: Interactive query\n", + "# Uncomment and modify for your custom query\n", + "\n", + "custom_query = \"What are the best practices for RAG systems?\"\n", + "\n", + "print(\"=\" * 60)\n", + "print(\"Running RAG Query\")\n", + "print(\"=\" * 60)\n", + "print(f\"\\nQuery: {custom_query}\\n\")\n", + "\n", + "result = rag_pipeline(custom_query)\n", + "\n", + "print(\"Response:\")\n", + "print(\"-\" * 60)\n", + "print(result['response'])\n", + "print(\"-\" * 60)\n", + "\n", + "print(f\"\\nContext Documents ({len(result['context'])}):\")\n", + "for i, doc in enumerate(result['context'], 1):\n", + " print(f\"\\n{i}. {doc['id']}\")\n", + " print(f\" Similarity Score: {doc['score']:.4f}\")\n", + " print(f\" Preview: {doc['content'][:100]}...\")\n", + "\n", + "print(f\"\\nSession ID: {result['session_id']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ba1da346", + "metadata": {}, + "source": [ + "## 13. Best Practices & Troubleshooting\n", + "\n", + "### Vector Quality Tips\n", + "- Use consistent embedding model throughout pipeline\n", + "- Normalize embeddings for accurate similarity search\n", + "- Test embedding quality with known good vs. bad examples\n", + "\n", + "### Chunk Management\n", + "- Optimal chunk size: 512-1000 tokens\n", + "- Overlap chunks by 20-30% for context continuity\n", + "- Store chunk metadata for result filtering\n", + "\n", + "### Performance Optimization\n", + "- Create appropriate indexes on vector columns\n", + "- Use batch operations for large document sets\n", + "- Cache frequently accessed embeddings\n", + "- Monitor Bedrock token usage for cost optimization\n", + "\n", + "### Security Best Practices\n", + "- Use IAM roles instead of access keys\n", + "- Encrypt Oracle database connections (SSL/TLS)\n", + "- Implement VPC endpoints for Bedrock access\n", + "- Audit and log all RAG queries" + ] + }, + { + "cell_type": "markdown", + "id": "6de0853b", + "metadata": {}, + "source": [ + "### Troubleshooting Common Issues" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f1f1eb0", + "metadata": {}, + "outputs": [], + "source": [ + "def troubleshoot_connectivity():\n", + " \"\"\"Diagnose connection issues\"\"\"\n", + " print(\"Troubleshooting: Connection Tests\")\n", + " print(\"=\" * 60)\n", + " \n", + " # Test database\n", + " print(\"\\n1. Testing Oracle Database Connection...\")\n", + " try:\n", + " cursor = oracle_connection.cursor()\n", + " cursor.execute(\"SELECT 1 FROM DUAL\")\n", + " result = cursor.fetchone()\n", + " cursor.close()\n", + " print(\" ✓ Oracle database connection successful\")\n", + " except Exception as e:\n", + " print(f\" ✗ Oracle database error: {e}\")\n", + " print(\" Check: hostname, port, credentials, network connectivity\")\n", + " \n", + " # Test Bedrock\n", + " print(\"\\n2. Testing Bedrock Connection...\")\n", + " try:\n", + " bedrock_client.list_foundation_models()\n", + " print(\" ✓ Bedrock connection successful\")\n", + " except Exception as e:\n", + " print(f\" ✗ Bedrock error: {e}\")\n", + " print(\" Check: AWS credentials, region, IAM permissions\")\n", + " \n", + " # Test embedding\n", + " print(\"\\n3. Testing Embedding Generation...\")\n", + " try:\n", + " test_embed = generate_embedding(\"test\")\n", + " print(f\" ✓ Embedding successful ({len(test_embed)} dimensions)\")\n", + " except Exception as e:\n", + " print(f\" ✗ Embedding error: {e}\")\n", + " print(\" Check: Bedrock model availability, IAM permissions\")\n", + "\n", + "# Run troubleshooting\n", + "# troubleshoot_connectivity()\n", + "\n", + "def check_embedding_dimensions():\n", + " \"\"\"Verify embedding dimensions match vector column\"\"\"\n", + " embedding = generate_embedding(\"test\")\n", + " dim = len(embedding)\n", + " expected_dim = CONFIG['EMBEDDING_DIMENSION']\n", + " \n", + " print(f\"Embedding Dimensions Check:\")\n", + " print(f\" Actual: {dim}\")\n", + " print(f\" Expected: {expected_dim}\")\n", + " \n", + " if dim == expected_dim:\n", + " print(\" ✓ Dimensions match\")\n", + " else:\n", + " print(\" ✗ Dimension mismatch! Update vector column definition\")\n", + " return False\n", + " \n", + " return True\n", + "\n", + "# check_embedding_dimensions()" + ] + }, + { + "cell_type": "markdown", + "id": "ea2fb9a3", + "metadata": {}, + "source": [ + "## 14. Cleanup & Summary\n", + "\n", + "### Summary\n", + "You have successfully built a complete RAG system that:\n", + "- ✓ Integrates Oracle Database with AWS Bedrock\n", + "- ✓ Generates and stores vector embeddings\n", + "- ✓ Performs similarity search on document vectors\n", + "- ✓ Grounds LLM responses with retrieved context\n", + "- ✓ Tracks RAG sessions and conversation history\n", + "\n", + "### Next Steps\n", + "1. **Add More Documents**: Ingest your own documents using `ingest_documents()`\n", + "2. **Deploy to Production**: Use EC2/Lambda with Auto Scaling\n", + "3. **Build UI**: Create Streamlit or FastAPI application\n", + "4. **Monitor**: Set up CloudWatch endpoints and alarms\n", + "5. **Optimize**: Implement caching and load balancing for high volume\n", + "\n", + "### Resources\n", + "- [Oracle AI Database 26ai Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/26/)\n", + "- [Amazon Bedrock User Guide](https://docs.aws.amazon.com/bedrock/latest/userguide/)\n", + "- [Oracle Vector Database](https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_VECTOR.html)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2f87d64", + "metadata": {}, + "outputs": [], + "source": [ + "def cleanup():\n", + " \"\"\"Clean up resources\"\"\"\n", + " print(\"Cleaning up resources...\")\n", + " \n", + " try:\n", + " if oracle_connection:\n", + " oracle_connection.close()\n", + " print(\"✓ Oracle connection closed\")\n", + " except Exception as e:\n", + " print(f\"✗ Error closing connection: {e}\")\n", + "\n", + "# Display final statistics\n", + "print(\"\\n\" + \"=\" * 60)\n", + "print(\"RAG System Status\")\n", + "print(\"=\" * 60)\n", + "\n", + "stats = get_database_stats()\n", + "print(f\"\\nTotal Documents Stored: {stats['total_documents']}\")\n", + "print(f\"Total Sessions: {stats['total_sessions']}\")\n", + "\n", + "print(\"\\n✓ RAG System Ready!\")\n", + "print(\"\\nTo perform a RAG query, use: rag_pipeline(your_question)\")\n", + "print(\"To ingest more documents, use: ingest_documents(text, source='name')\")\n", + "print(\"To cleanup resources, call: cleanup()\")\n", + "\n", + "# Note: Do not call cleanup() unless you want to end the session\n", + "# cleanup()" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/multicloud-oracledb-at-aws/oracle-db-aws-bedrock.md b/notebooks/multicloud-oracledb-at-aws/oracle-db-aws-bedrock.md new file mode 100644 index 00000000..e2333e97 --- /dev/null +++ b/notebooks/multicloud-oracledb-at-aws/oracle-db-aws-bedrock.md @@ -0,0 +1,499 @@ +# Oracle Database with AWS Bedrock for RAG Search + +Integrate Oracle Database with Amazon Bedrock to build powerful Retrieval-Augmented Generation (RAG) applications. This guide demonstrates how to leverage Oracle's vector storage capabilities with AWS Bedrock's foundation models for intelligent, context-aware applications. + +## Table of Contents +- [Overview](#overview) +- [Prerequisites](#prerequisites) +- [Architecture](#architecture) +- [Setup Instructions](#setup-instructions) +- [Implementation Guide](#implementation-guide) +- [Code Examples](#code-examples) +- [Best Practices](#best-practices) +- [Troubleshooting](#troubleshooting) + +## Overview + +This integration combines: +- **Oracle AI Database 26ai**: A powerful vector database for storing and searching embeddings +- **Amazon Bedrock**: Serverless foundation models for text embeddings and LLM inference +- **RAG Pattern**: Ground LLM responses with relevant context from your Oracle database + +### Key Benefits +- **Low-Latency Access**: Use Oracle Database@AWS for direct connectivity +- **Secure Vector Storage**: Native AI vector search in Oracle AI Database 26ai +- **Scalable Embeddings**: Leverage Amazon Titan or other Bedrock models for embedding generation +- **Intelligent Responses**: Ground Claude or other LLMs with your proprietary data + +## Prerequisites + +### AWS Resources Required +- AWS Account with Bedrock access enabled +- EC2 instance or Lambda function in same VPC region +- IAM role with bedrock:InvokeModel permissions +- Amazon Bedrock models enabled: + - `amazon.titan-embed-text-v1` or `amazon.titan-embed-text-v2` (embeddings) + - `anthropic.claude-3-5-sonnet-20241022` or similar (LLM) + +### Oracle Database Requirements +- Oracle AI Database 26ai or Autonomous Database@AWS +- Network connectivity from compute resource to database +- APEX 24.1+ or ORDS for REST API access (optional) + +### Python Dependencies +```bash +pip install python-oracledb boto3 langchain-aws langchain-oracle langchain-text-splitters +``` + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Application Layer │ +│ (Python/Streamlit/FastAPI) │ +└────────────────────┬────────────────────────────────────────┘ + │ + ┌────────────┴────────────┐ + │ │ +┌───────▼──────────┐ ┌──────▼──────────────┐ +│ Oracle Vector │ │ Amazon Bedrock │ +│ Store (AI DB) │ │ - Embeddings │ +│ - Store vectors │ │ - LLM Inference │ +│ - Similarity │ │ - Model Selection │ +│ search │ │ │ +└──────────────────┘ └────────────────────┘ +``` + +### Data Flow +1. **Ingestion**: Documents → Split into chunks → Generate embeddings (Bedrock) → Store in Oracle +2. **Query**: User query → Generate embedding (Bedrock) → Search Oracle → Retrieve context +3. **Generation**: Context + Query → Send to LLM (Bedrock) → Generate response + +## Setup Instructions + +### 1. Prepare Oracle Database + +Connect to your Oracle Database and create vector storage: + +```sql +-- Create table for document chunks and vectors +CREATE TABLE documents ( + doc_id VARCHAR2(256) PRIMARY KEY, + content CLOB, + embedding VECTOR(1536), + metadata JSON, + created_at TIMESTAMP DEFAULT SYSTIMESTAMP +); + +-- Create index for vector similarity search +CREATE INDEX doc_vector_idx ON documents(embedding) + INDEXTYPE IS VECTOR_INDEX; + +-- Create table for RAG session history (optional) +CREATE TABLE rag_sessions ( + session_id VARCHAR2(256) PRIMARY KEY, + query VARCHAR2(4000), + response CLOB, + context CLOB, + model_id VARCHAR2(256), + created_at TIMESTAMP DEFAULT SYSTIMESTAMP +); +``` + +### 2. Configure AWS Credentials + +Set up your AWS credentials for Bedrock access: + +```bash +# Option 1: Environment variables +export AWS_ACCESS_KEY_ID="your_access_key" +export AWS_SECRET_ACCESS_KEY="your_secret_key" +export AWS_REGION="us-east-1" + +# Option 2: AWS CLI configuration +aws configure +``` + +Verify Bedrock access: +```bash +aws bedrock list-foundation-models --region us-east-1 +``` + +### 3. Set Up Network Connectivity + +For Oracle Database@AWS: +```bash +# Create ODB Network peering to your VPC +# Document the database hostname, port, and credentials +# Test connectivity: +nc -zv 1522 +``` + +## Implementation Guide + +### Step 1: Document Ingestion Pipeline + +Load documents, chunk them, and generate embeddings: + +```python +import boto3 +import oracledb +from langchain.text_splitter import RecursiveCharacterTextSplitter + +# Initialize Bedrock client for embeddings +bedrock_client = boto3.client('bedrock-runtime', region_name='us-east-1') + +# Initialize Oracle connection +connection = oracledb.connect( + user="admin", + password="your_password", + dsn="your_db_host:1522/FREEPDB1" +) + +def generate_embedding(text: str) -> list: + """Generate embedding using Amazon Titan""" + response = bedrock_client.invoke_model( + modelId='amazon.titan-embed-text-v2:0', + contentType='application/json', + accept='application/json', + body=json.dumps({ + "inputText": text, + "dimensions": 1536, + "normalize": True + }) + ) + return json.loads(response['body'].read())['embedding'] + +def ingest_documents(file_path: str): + """Ingest documents into Oracle vector store""" + # Read documents + with open(file_path, 'r') as f: + text = f.read() + + # Split into chunks + splitter = RecursiveCharacterTextSplitter( + chunk_size=1000, + chunk_overlap=200 + ) + chunks = splitter.split_text(text) + + # Generate embeddings and store + cursor = connection.cursor() + for i, chunk in enumerate(chunks): + embedding = generate_embedding(chunk) + doc_id = f"doc_{i}_{hash(chunk) % 10000}" + + cursor.execute( + """ + INSERT INTO documents (doc_id, content, embedding, metadata) + VALUES (:1, :2, :3, :4) + """, + [doc_id, chunk, embedding, json.dumps({"chunk_index": i})] + ) + + connection.commit() + cursor.close() + print(f"Ingested {len(chunks)} document chunks") + +# Run ingestion +ingest_documents("sample_documents.txt") +``` + +### Step 2: RAG Retrieval + +Retrieve relevant context from Oracle: + +```python +def retrieve_context(query: str, top_k: int = 3) -> list: + """Retrieve relevant documents from Oracle vector store""" + # Generate query embedding + query_embedding = generate_embedding(query) + + # Perform similarity search in Oracle + cursor = connection.cursor() + cursor.execute( + """ + SELECT doc_id, content, VECTOR_DISTANCE(embedding, :1) AS distance + FROM documents + ORDER BY distance + FETCH FIRST :2 ROWS ONLY + """, + [query_embedding, top_k] + ) + + results = cursor.fetchall() + cursor.close() + + return [{"id": r[0], "content": r[1], "score": float(r[2])} for r in results] + +# Test retrieval +context = retrieve_context("How do I configure Oracle with AWS?") +for doc in context: + print(f"Doc: {doc['id']} (Score: {doc['score']:.4f})") + print(f"Content: {doc['content'][:200]}...\n") +``` + +### Step 3: LLM Response Generation + +Use Bedrock to generate responses grounded in retrieved context: + +```python +def generate_rag_response(query: str, context_docs: list) -> str: + """Generate response using Bedrock LLM with context""" + # Prepare context string + context_str = "\n\n".join([f"[{doc['id']}]\n{doc['content']}" + for doc in context_docs]) + + # Prepare prompt + system_prompt = """You are a helpful assistant that answers questions based on the provided context. + Always reference the source document when applicable. + If the context doesn't contain relevant information, say so clearly.""" + + user_message = f"""Context: +{context_str} + +Question: {query} + +Please provide a comprehensive answer based on the context above.""" + + # Invoke Bedrock LLM + response = bedrock_client.invoke_model( + modelId='anthropic.claude-3-5-sonnet-20241022', + contentType='application/json', + accept='application/json', + body=json.dumps({ + "anthropic_version": "bedrock-2023-06-01", + "max_tokens": 1024, + "system": system_prompt, + "messages": [ + { + "role": "user", + "content": user_message + } + ] + }) + ) + + response_text = json.loads(response['body'].read())['content'][0]['text'] + return response_text + +# Test RAG pipeline +query = "What are best practices for Oracle and AWS integration?" +context = retrieve_context(query) +response = generate_rag_response(query, context) +print(f"Query: {query}\n\nResponse:\n{response}") +``` + +### Step 4: Complete RAG Application + +Integrate all components: + +```python +def rag_pipeline(query: str, top_k: int = 3) -> dict: + """Complete RAG pipeline""" + # Retrieve context + context_docs = retrieve_context(query, top_k) + + if not context_docs: + return { + "query": query, + "response": "No relevant context found in the knowledge base.", + "context": [] + } + + # Generate response + response = generate_rag_response(query, context_docs) + + # Store in session history + session_id = f"session_{int(time.time())}" + cursor = connection.cursor() + cursor.execute( + """ + INSERT INTO rag_sessions + (session_id, query, response, context, model_id) + VALUES (:1, :2, :3, :4, :5) + """, + [ + session_id, + query, + response, + json.dumps([{"id": d["id"], "score": d["score"]} for d in context_docs]), + "anthropic.claude-3-5-sonnet-20241022" + ] + ) + connection.commit() + cursor.close() + + return { + "session_id": session_id, + "query": query, + "response": response, + "context": context_docs + } + +# Test complete pipeline +result = rag_pipeline("How do I optimize vector search in Oracle?") +print(json.dumps(result, indent=2)) +``` + +## Code Examples + +### Using LangChain for RAG + +```python +from langchain_oracle import OracleVectorStore +from langchain_aws import BedrockEmbeddings, BedrockLLM +from langchain.chains import RetrievalQA + +# Initialize components +embeddings = BedrockEmbeddings( + region_name="us-east-1", + model_id="amazon.titan-embed-text-v2:0" +) + +llm = BedrockLLM( + region_name="us-east-1", + model_id="anthropic.claude-3-5-sonnet-20241022" +) + +vector_store = OracleVectorStore( + client=connection, + table_name="documents", + embedding_function=embeddings +) + +# Create RAG chain +qa_chain = RetrievalQA.from_chain_type( + llm=llm, + chain_type="stuff", + retriever=vector_store.as_retriever(search_kwargs={"k": 3}) +) + +# Query +response = qa_chain.run("How to integrate Oracle with AWS Bedrock?") +print(response) +``` + +### Streaming Responses + +```python +def stream_rag_response(query: str): + """Stream response for real-time user feedback""" + context_docs = retrieve_context(query) + context_str = "\n\n".join([f"[{doc['id']}]\n{doc['content']}" + for doc in context_docs]) + + user_message = f"""Context: +{context_str} + +Question: {query}""" + + # Stream response + with bedrock_client.invoke_model_with_response_stream( + modelId='anthropic.claude-3-5-sonnet-20241022', + contentType='application/json', + accept='application/json', + body=json.dumps({ + "anthropic_version": "bedrock-2023-06-01", + "max_tokens": 1024, + "messages": [{"role": "user", "content": user_message}] + }) + ) as response: + for event in response['body']: + if 'contentBlockDelta' in event: + print(event['contentBlockDelta']['delta']['text'], end='', flush=True) +``` + +## Best Practices + +### 1. Vector Quality +- Use consistent embedding model throughout pipeline +- Normalize embeddings for accurate similarity search +- Test embedding quality with known good vs. bad examples + +### 2. Chunk Management +- Optimal chunk size: 512-1000 tokens +- Overlap chunks by 20-30% for context continuity +- Store chunk metadata for result filtering + +### 3. Performance Optimization +- Create appropriate indexes on vector columns +- Use `VECTOR_DISTANCE` with batch operations +- Cache frequently accessed embeddings +- Monitor Bedrock token usage for cost optimization + +### 4. Security +- Use IAM roles instead of access keys +- Encrypt Oracle database connections (SSL/TLS) +- Implement VPC endpoints for Bedrock access +- Audit and log all RAG queries + +### 5. Quality Control +- Implement feedback loops to improve retrieval +- Monitor retrieval relevance scores +- A/B test different embedding models +- Track response latency and accuracy metrics + +## Troubleshooting + +### Connection Issues +```python +# Test database connectivity +try: + cursor = connection.cursor() + cursor.execute("SELECT 1 FROM DUAL") + print("Database connected") +except Exception as e: + print(f"Connection error: {e}") + +# Test Bedrock access +try: + bedrock_client.list_foundation_models() + print("Bedrock accessible") +except Exception as e: + print(f"Bedrock error: {e}") +``` + +### Vector Dimension Mismatch +```python +# Verify embedding dimensions +def check_embedding_dimensions(): + embedding = generate_embedding("test") + print(f"Embedding dimensions: {len(embedding)}") + # Should match vector column definition (usually 1536 for Titan) +``` + +### Slow Retrieval +```sql +-- Check index usage +SELECT * FROM user_indexes WHERE table_name = 'DOCUMENTS'; + +-- Analyze vector column statistics +ANALYZE TABLE documents COMPUTE STATISTICS; + +-- Monitor query performance +EXPLAIN PLAN FOR +SELECT doc_id FROM documents +ORDER BY VECTOR_DISTANCE(embedding, :1) +FETCH FIRST 10 ROWS ONLY; +``` + +### High Bedrock Costs +- Batch embeddings when possible +- Cache embeddings for repeated queries +- Use smaller models for testing +- Monitor token usage with CloudWatch + +## Next Steps + +1. **Deploy to Production**: Use EC2/Lambda with Auto Scaling +2. **Build UI**: Create Streamlit or FastAPI application +3. **Monitor**: Set up CloudWatch endpoints and alarms +4. **Iterate**: Collect user feedback and improve retrieval quality +5. **Scale**: Implement caching and load balancing for high volume + +## References +- [Oracle AI Database 26ai Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/26/) +- [Amazon Bedrock User Guide](https://docs.aws.amazon.com/bedrock/latest/userguide/) +- [Oracle Vector Database](https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_VECTOR.html) + diff --git a/notebooks/multicloud-oracledb-at-aws/oracle-db-exadata.md b/notebooks/multicloud-oracledb-at-aws/oracle-db-exadata.md new file mode 100644 index 00000000..ea16ddf6 --- /dev/null +++ b/notebooks/multicloud-oracledb-at-aws/oracle-db-exadata.md @@ -0,0 +1,981 @@ +# Provision Oracle Exadata Database Service in Oracle Database@AWS + +A comprehensive step-by-step guide to provisioning Oracle Exadata Database Service on Dedicated Infrastructure within AWS, enabling low-latency access to Oracle databases from AWS applications. + +## Overview + +Oracle Database@AWS is a strategic partnership between Oracle and Amazon Web Services that enables applications running in AWS Regions to use Oracle Exadata Database Service on Dedicated Infrastructure. This integration provides: + +- **Low-latency access** from AWS to Oracle databases +- **Native AWS integration** with Amazon EC2, Kinesis, QuickSight, and other services +- **Oracle Database 23ai features** including AI Vector Search +- **Direct network connectivity** between VPC applications and ODB network + +### Architecture + +The architecture consists of: +- **AWS VPC** - Your application servers and AWS resources +- **ODB Network** - Private isolated network hosting OCI infrastructure +- **Exadata Infrastructure** - Database and storage servers with RDMA networking +- **Exadata VM Clusters** - Virtual machines hosting your Oracle databases +- **ODB Peering** - Secure connection between VPC and ODB network + +## Prerequisites + +1. **AWS Account** + - Active AWS account with required permissions + - AWS Management Console access + +2. **Oracle Database@AWS Access** + - Private offer from AWS Marketplace + - Account activation by AWS/Oracle sales team + - Estimated activation time: 1-2 business days + +3. **Network Planning** + - VPC CIDR range documented + - Availability Zone selected for deployment + - EC2 instance security groups identified + +4. **Oracle License** + - Bring Your Own License (BYOL) - recommended for existing customers + - Or prepare for License Included option + +5. **Credentials & Access** + - SSH key pairs for VM cluster access + - Administrator credentials for database + - Backup storage configuration (S3 or OCI Object Storage) + +## Task 1: Create the ODB Network + +An ODB Network is a private, isolated network that hosts OCI infrastructure within an AWS Availability Zone, serving as the communication bridge between AWS and OCI. + +### Step-by-Step Instructions + +1. **Log in to AWS Management Console** + - Open [AWS Management Console](https://console.aws.amazon.com/) + - Sign in with your AWS credentials + - Navigate to [Oracle Database@AWS Console](https://console.aws.amazon.com/odb/) + +2. **Start ODB Network Creation** + - In the left navigation, click **ODB networks** + - Click **Create ODB network** button + - Or directly from dashboard, under Step 1, click **Create ODB network** + +3. **Enter Network Configuration** + + Complete the Create ODB Network form with: + + **Basic Information:** + - **ODB Network Name**: Descriptive name (e.g., `prod-exadata-network`, `dev-odb-network`) + - **Availability Zone**: Select target AZ (same as where you plan Exadata infrastructure) + - **Description** (optional): Purpose and notes + + **Network CIDR Ranges:** + - **Client Connection CIDR**: IP range for EC2 application servers + - Example: `192.168.0.0/16` + - Must be /24 or larger + - Ensure no overlap with existing VPC ranges + + - **Backup Connection CIDR**: IP range for backup traffic to S3/OCI Object Storage + - Example: `192.169.0.0/16` + - Must be /24 or larger + - Should differ from Client CIDR + + **Domain Configuration:** + - **Domain Name Prefix** (optional): Custom domain prefix + - Example: `mycompany` + - Final FQDN will be: `mycompany.oraclevcn.com` + - If not provided, uses auto-generated prefix + +4. **Configure Advanced Options** (Optional) + + - **S3 Backup Access**: Enable if backing up to AWS S3 + - **Zero-ETL Integration**: Enable for Amazon Redshift analytics + - **VPC Endpoint Configuration**: For AWS service connectivity + - **Tags**: Add tags for cost tracking and resource management + +5. **Review and Create** + - Review all entered information + - Verify CIDR ranges don't conflict with existing networks + - Click **Create ODB network** + - **Expected time**: 10-15 minutes + - Status visible in dashboard transitions to "Available" + +### Network Details Checklist + +After successful creation, document the following: + +``` +ODB Network Details: +├── Network ID: odb-xxxxxxxx (auto-generated) +├── Network Name: prod-exadata-network +├── Region: us-east-1 +├── Availability Zone: us-east-1a +├── Client Connection CIDR: 192.168.0.0/16 +├── Backup Connection CIDR: 192.169.0.0/16 +├── Domain Prefix: mycompany +├── Full Domain: mycompany.oraclevcn.com +├── Status: Available +└── Creation Time: YYYY-MM-DD HH:MM UTC +``` + +### Configure ODB Peering to VPC + +After ODB network is created, set up peering to enable EC2 access: + +1. **Navigate to ODB Peering** + - From ODB Network details, click **Peering** tab + - Note **Local Gateway Route Table ID** + - Copy **ODB Network Client CIDR** + +2. **Update EC2 VPC Route Tables** + - Open EC2 console → VPC → Route Tables + - Find your application's route table + - Click **Edit routes** + - Add route: + - Destination: ODB Client CIDR (e.g., 192.168.0.0/16) + - Target: Local Gateway Route Table (from peering info) + - Save changes + +3. **Verify Connectivity** + ```bash + # From EC2 instance in VPC + ping .oraclevcn.com + nslookup mycompany.oraclevcn.com + ``` + +## Task 2: Create the Exadata Infrastructure + +The Exadata Infrastructure is the underlying hardware architecture containing database servers, storage servers, and high-speed RDMA networking. + +### Exadata Infrastructure Configuration + +1. **Access Exadata Infrastructure Creation** + - From ODB Dashboard, click **Exadata Infrastructures** + - Click **Create Exadata Infrastructure** + +2. **Step 1: Configure General Settings** + + - **Infrastructure Name**: Descriptive name + - Example: `prod-exadata-01`, `exadata-primary` + - Used for identification and resource tagging + + - **Availability Zone**: Select same AZ as ODB network + - Must match ODB network AZ for optimal performance + + - **Display Name** (optional): Human-readable name for reporting + + - Click **Next** to proceed + +3. **Step 2: Configure Exadata Infrastructure Shape** + + Select infrastructure size based on workload requirements: + + **Minimum Configuration (Pre-filled):** + - Database Servers: 2 + - Storage Servers: 3 + - Total Storage: 240 TB (80 TB per storage server) + + **Scaling Options:** + - **Database Servers**: 2-32 maximum + - Add servers for higher CPU/memory needs + - Each additional server adds compute capacity + + - **Storage Servers**: 3-64 maximum + - Minimum 3 for data redundancy + - Each additional server adds 80 TB storage + - Example: 8 DB servers + 12 storage servers = 960 TB total + + **Sizing Guidelines:** + + | Workload | DB Servers | Storage | Use Case | + |----------|-----------|---------|----------| + | Dev/Test | 2 | 240 TB | Non-production testing | + | Small Prod | 4 | 320 TB | Single small application | + | Medium Prod | 8 | 640 TB | Multiple applications | + | Large Prod | 16+ | 960+ TB | Enterprise deployments | + + > **Note**: Verify that your OCI tenancy limits allow the selected configuration + + - Click **Next** to proceed + +4. **Step 3: Configure Maintenance and Tags** + + **Maintenance Schedule Configuration:** + + - **Patching Mode**: + - **Oracle-managed** (default) - Oracle manages schedule and patching + - **Customer-managed** - You control maintenance window timing + + - If Customer-managed selected: + - **Month**: Select month within quarter + - **Week**: Select week of month (1st, 2nd, 3rd, 4th, last) + - **Day of Week**: Select preferred day (Mon-Sun) + - **Starting Hour**: Time maintenance can begin + - **Notification Days**: How far in advance to notify (0-30 days) + + - **Pre-Maintenance Timeout** (optional): + - Time to wait before starting maintenance + - Allows manual checks or script execution + - Range: 0-120 minutes + + **Notification Contacts:** + - **Email Addresses**: Up to 10 email addresses + - Receives maintenance notifications + - Example: `dba@company.com`, `ops-team@company.com` + + **Tagging** (optional but recommended): + - **Environment**: `production`, `staging`, `development` + - **Owner**: Team or department name + - **Application**: Associated application name + - **CostCenter**: Billing allocation code + - **BackupStrategy**: Backup requirements + - Custom tags as needed + + > **Note**: Maintenance settings can be updated from OCI Console later. You cannot modify infrastructure configuration after creation from AWS Console. + + - Click **Next** to proceed + +5. **Step 4: Review and Create** + + **Review Section Contains:** + - Infrastructure name and AZ + - Database server count + - Storage server count and capacity + - Maintenance preferences + - Tags + + **Before Creating:** + - Verify all settings are correct + - Confirm no configuration changes needed + - Ensure sufficient quota in OCI tenancy + + **Options:** + - Click **Previous** to go back and modify settings + - Click **Cancel** to abandon creation + - Click **Create Exadata Infrastructure** to proceed + +6. **Creation Progress** + + - **Expected Duration**: 45-60 minutes + - **Status Changes**: + - Creating → Provisioning database/storage servers + - Available → Infrastructure ready for VM clusters + - **Monitor**: Dashboard shows real-time status + - **Notifications**: Sent to configured email addresses + +7. **Post-Creation - Exadata Infrastructure Summary** + + After successful creation, access the Summary tab to view: + + **Summary Information:** + - **Configuration**: View all infrastructure settings + - **Database Servers**: List of servers with specifications + - **Exadata VM Clusters**: Clusters created on this infrastructure + - **Autonomous VM Clusters**: Any autonomous VM clusters + - **OCI Maintenance**: Link to OCI Console for maintenance updates + - **Tags**: Associated tags for organization + + **Key Information to Save:** + - Infrastructure ID + - Database server count and configuration + - Storage server count and total capacity + - Maintenance window settings + - OCI Console link for additional management + +## Task 3: Create an Exadata VM Cluster + +An Exadata VM Cluster is a set of virtual machines on the Exadata Infrastructure that will host your Oracle databases. + +### Create Exadata VM Cluster + +1. **Start VM Cluster Creation** + - From ODB Dashboard, click **Exadata VM Clusters** + - Click **Create VM Cluster** + +2. **Step 1: Configure General Settings** + + - **VM Cluster Name**: Descriptive identifier + - Example: `prod-vmcluster-01`, `exadata-cluster-primary` + - Must be unique within Exadata Infrastructure + + - **Time Zone**: Database server timezone + - Default: UTC + - Examples: `America/New_York`, `Europe/London`, `Asia/Tokyo` + - Used for database scheduling + + - **Time Zone for Reports** (optional): Separate timezone for reporting + - If different from database timezone + + - **License Options**: Choose your licensing model + - **Bring Your Own License (BYOL)** + - Use existing Oracle Enterprise licenses + - Best for existing Oracle customers + - Reduces software costs + - Requires Oracle license documentation + + - **License Included** + - Oracle provides all licenses + - Simpler procurement + - Higher per-unit cost + - No separate licensing required + + - Click **Next** to proceed + +3. **Step 2: Configure Infrastructure Settings** + + - **Select Exadata Infrastructure** + - Dropdown: Choose infrastructure created in Task 2 + - Displays available capacity + - VM cluster will be deployed on this infrastructure + + - **Grid Infrastructure Version**: Select Oracle version + - Options: 19c, 21c, 23ai (when available) + - Determines supported Oracle Database versions + - Example: Grid 23c required for Oracle Database 23ai + + - **Exadata Image Version**: Select OS/system software version + - Latest version recommended + - Determines OS version and available features + - Includes OS updates and driver versions + + - **Database Servers**: Select which DB servers to use + - Checkbox list of available servers + - Example: Select servers 1, 2, 3 for this cluster + - Multiple clusters can span same infrastructure + + **VM Configuration** - Allocate resources per VM: + + - **CPU Core Count** (OCPUs): + - Minimum: 2 OCPUs per VM + - Examples: 4, 8, 16, 20, 32 OCPUs + - Allocates this many cores to each VM + + - **Memory per VM**: + - Minimum: 30 GB per VM + - Typical: 60 GB, 128 GB, 256 GB+ + - Each VM gets this much RAM + + - **Local Storage per VM**: + - Minimum: 60 GB per VM + - Typical: 100 GB, 200 GB, 500 GB+ + - Used for database files and logs + + **Storage Configuration** - Cluster-level storage allocation: + + - **Total Exadata Storage**: Enter in multiples of 1 TB + - Minimum: 2 TB + - Typical: 50 TB, 100 TB, 500 TB+ + - Shared among all VMs in cluster + + - **Enable Local Backups Storage** (optional): + - Allows database backups to local Exadata storage + - **Cannot be changed after creation** + - Consider if planning local backup strategy + + - **Enable Sparse Snapshots** (optional): + - Enables snapshot functionality + - **Cannot be changed after creation** + - For point-in-time recovery strategies + + - Click **Next** to proceed + +4. **Step 3: Configure Connectivity** + + - **Select ODB Network** + - Dropdown: Choose ODB network created in Task 1 + - Provides network isolation and peering + + - **Host Name Prefix**: + - Prefix for VM hostnames + - Example: `vmcluster01` → `vmcluster01-1.oraclevcn.com`, etc. + - Used for DNS resolution + + - **SSH Key Pairs** (Important for access): + - Click **Add SSH Key** to add public keys + - Paste public key content (RSA format) + - One key per button click + - Multiple keys allow multiple users SSH access + - Required for VM administration + - Store private keys securely + - Example: + ``` + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC... user@hostname + ``` + + - **SCAN Listener Port** (optional): + - Single Client Access Name listener port + - Default: 1521 (standard Oracle port) + - Custom range: 1024-8999 + - Used for database connections + + - Click **Next** to proceed or **Skip to Review and Create** + +5. **Step 4: Configure Diagnostics and Tags** + + **Diagnostics** (optional): + - **Enable OCI Diagnostic Collection**: + - Oracle collects guest VM diagnostics + - Helps identify and resolve issues quickly + - Can be disabled anytime + - Checkbox: Enable/Disable + + - **Subscribe to Events**: + - Get notifications on resource state changes + - Alerts for VM or cluster state changes + - Requires notification configuration + + **Tags** (optional but recommended): + - **Tag Namespace**: Select or create namespace + - **Tag Key**: Descriptive key (e.g., `Environment`) + - **Tag Value**: Tag value (e.g., `production`) + - **Multiple Tags**: Click **Add another tag** for more + + Example tags: + - `Environment: production` + - `Application: ERP` + - `Owner: DBA-Team` + - `CostCenter: 12345` + - `BackupPolicy: Daily` + + - Click **Next** to proceed + +6. **Step 5: Review and Create** + + **Review All Settings:** + - VM cluster name and timezone + - Infrastructure and grid version selection + - CPU, memory, and storage allocation + - ODB network and SSH keys + - Diagnostics and tags + + **Before Creating:** + - Verify CPU/memory allocation adequate for workload + - Confirm SSH keys are correct + - Check storage allocation sufficient + - Ensure tags correctly categorize resource + + **Options:** + - Click **Previous** to modify settings + - Click **Cancel** to abandon creation + - Click **Create VM Cluster** to proceed + +7. **Creation Progress and Monitoring** + + - **Expected Duration**: Up to 6 hours (depends on size) + - **Status Progression**: + - Creating (0-2 hours): Initial infrastructure setup + - Available (2-6 hours): Configuring database servers + - Ready (6 hours): VM cluster operational + - **Monitor**: Dashboard shows real-time progress + - **Notifications**: Email alerts on status changes + +### Post-Creation VM Cluster Summary + +After successful creation, access detailed information: + +**Summary Tab Contains:** + +- **Configuration**: + - VM cluster name and ID + - Grid Infrastructure version + - Database server count + - CPU/memory/storage per VM + - Total cluster storage + +- **Connectivity**: + - Hostname prefix + - SSH public keys + - SCAN listener port + - ODB network association + +- **Monitoring**: + - CPU utilization metrics + - Memory utilization + - Storage usage + - Load average + - Network metrics + +- **OCI Resources**: + - Direct link to OCI Console + - Access for database creation + - Infrastructure details + +**Save Key Information:** +- VM Cluster ID +- Hostname pattern for DNS +- SCAN listener port +- Grid Infrastructure version +- Storage configuration details + +## Task 4: Create Oracle Database + +Oracle Database creation is managed from the OCI Console. With tight AWS-OCI integration, you can navigate directly from AWS Console. + +### Create Your First Database + +1. **Navigate to OCI Console** + + **From AWS Console:** + - Open your VM Cluster details + - Click **VM cluster name** or **Manage in OCI** button + - You'll be redirected to OCI Console + - Selected VM cluster will be pre-selected + + **Direct OCI Access:** + - If you have OCI Console access, navigate directly + - Go to **Databases** → **Exadata VM Clusters** + - Select your VM cluster + +2. **Start Database Creation** + + In the VM Cluster Details page: + - Click **Databases** tab + - Click **Create database** button + - Database creation form opens + +3. **Step 1: Basic Information** + + **Provide Database Name:** + - **Database Name**: Unique identifier for database + - Maximum: 8 characters + - Only alphanumeric characters + - Must begin with alphabetic character + - Example: `PRODDB`, `APPSDB`, `FINDB` + + **Provide Unique Name** (optional): + - Auto-generated if not specified + - Format: `_<3_char_unique>_` + - If you provide custom name: + - Maximum: 30 characters + - Alphanumeric or underscore (_) only + - Begin with alphabetic character + - Unique across VM cluster + + **Select Database Version**: + - Available versions: 19c, 21c, 23ai (when available) + - Example: Select Oracle Database 23ai for latest features + - 23ai includes AI Vector Search for semantic search + + **Provide PDB Name** (optional): + - Pluggable Database name + - Maximum: 8 characters + - Alphanumeric or underscore (_) + - Begins with alphabetic character + - Auto-generated if not specified + - Example: `PDB01`, `APPPDB` + - **Important**: Must be unique across VM cluster to avoid service name collisions + + **Database Home Source**: + - **Select Existing Database Home** + - Use previously created home + - Faster deployment + + - **Create New Database Home** + - Create dedicated home for this database + - Specify: **Database Home Display Name** + - Example: `PRODDB-HOME-1` + + **Additional Options**: + - **Enable Unified Auditing**: Enable comprehensive audit logging + - **Database Image** (optional): + - Oracle-published image (default) + - Or custom database software image + +4. **Step 2: Create Administrator Credentials** + + **SYS Password Creation** (critical): + - **Password Requirements**: + - 9-30 characters length + - At least 2 UPPERCASE characters + - At least 2 lowercase characters + - At least 2 NUMERIC characters + - At least 2 SPECIAL characters + - Special characters allowed: `_`, `#`, `-` + - Cannot contain username (SYS, SYSTEM, etc.) + - Cannot contain "Oracle" forward or reversed + + - **Example Strong Password**: + ``` + MyPro_D#01xABC + (meets: 14 chars, 2+ upper/lower/numbers/special) + ``` + + - **Confirm Password**: Reenter password for verification + + **Optional Security Enhancement**: + - Checkbox: **Use this password for TDE wallet** + - TDE = Transparent Data Encryption + - Uses same password for encryption wallet + - Simplifies credential management + +5. **Step 3: Configure Database Backups** + + **Enable Automatic Backup**: + - Checkbox: **Enable Automatic Backups** + - Recommended for production databases + - Automatic daily backup schedule + + **Backup Destination**: + - **Default**: Amazon S3 + - **Options**: S3 bucket or OCI Object Storage + - Specify S3 bucket name if using S3 + - Ensure bucket has proper permissions + + **Backup Schedule**: + - **Full Backup Schedule**: + - Day of Week: Select day for weekly full backup + - Example: Sunday (off-peak time) + - Time Window: Select backup time window + - Example: 02:00-04:00 UTC (midnight EST) + + - **Incremental Backup Schedule**: + - Time Window: For daily incremental backups + - Example: 23:00-01:00 UTC + + **Deletion Options After Termination**: + - **Backup Retention**: Specify retention if DB is deleted + - Options: Keep, delete after X days, or delete immediately + - Example: Keep backups for 30 days after termination + +6. **Advanced Options** (Optional but Recommended) + + Click **Show Advanced Options** to access additional settings: + + **Management Section**: + + - **Oracle SID Prefix** (optional): + - System ID for Oracle instance naming + - Maximum: 12 characters + - Alphanumeric or underscore + - Auto-generated from db_name if not specified + - Must be unique in VM cluster + - Example: `PROD`, `APP01` + + - **Character Set** (Database character encoding): + - Default: **AL32UTF8** (recommended) + - Supports all Unicode characters + - Changing after creation is complex + - Example alternatives: WE8ISO8859P1, UTF8 + + - **National Character Set** (optional): + - Default: AL16UTF16 + - For national language support + + **Encryption Section**: + + - **Key Management**: + - **Oracle-Managed Keys** (default) + - Oracle manages encryption keys + - Simpler, no user overhead + - Sufficient for most use cases + + - **Customer-Managed Keys** + - You manage encryption keys + - Full control over key lifecycle + - Requires separate key management + + **Tags Section**: + + - **Tag Namespace**: Select or create namespace + - **Tag Key/Value**: Add tags for organization + - **Purpose**: Cost allocation, compliance, automation + + Example Tags: + - `Environment: production` + - `Application: ERP-System` + - `DataClassification: Confidential` + - `BackupSLA: Daily` + - `Owner: Finance-Team` + +7. **Review and Create** + + **Final Review**: + - Verify database name and version + - Check SYS password requirements met + - Confirm backup configuration + - Review advanced options + - Check tags for accuracy + + **Validation**: + - Database name: Valid format, unique + - Password: Meets complexity requirements + - Storage: Sufficient space in VM cluster + - Network: Connectivity configured + + **Create Database**: + - Click **Create Database** button + - Provisioning begins + - **Expected Duration**: 15-30 minutes + - **Status**: Visible in database details + +### Monitor Database Creation + +1. **In OCI Console**: + - Navigate to **Databases** tab + - Select your database + - Status shows provisioning progress + - Transitions: Creating → Available → Ready + +2. **CloudWatch Metrics** (in AWS Console): + - CPU utilization increasing + - Memory allocation + - Network activity + - Storage status + +3. **Notifications**: + - Email alerts on creation completion + - Sent to configured contacts + +## Connect to Your Database + +Once the database is ready, establish connections from your applications. + +### Obtain Connection Information + +1. **From OCI Console**: + - Open your database details + - **Connection Information** tab shows: + - Service names (HIGH, MEDIUM, LOW) + - Connection strings + - SCAN listener details + +2. **Download Database Wallet**: + - If using SSL/TLS connections + - Contains certificates and connection info + - Secure method for external connections + +### Connection Methods + +#### Option A: SQL*Plus (Command Line) + +```bash +# Set TNS_ADMIN if using wallet +export TNS_ADMIN=/path/to/wallet + +# Connect with service name +sqlplus SYS@_MEDIUM + +# At password prompt, enter SYS password +``` + +#### Option B: SQL Developer (GUI) + +1. Create new connection: + - Username: `SYS` + - Password: Your administrator password + - Hostname: SCAN listener hostname + - Port: SCAN port (default 1521) + - Service: `_MEDIUM` + - Connection Type: Standard or Cloud Wallet + - Role: SYSDBA + +2. Test Connection → Connect + +#### Option C: Python (cx_Oracle) + +```python +import cx_Oracle + +# Connection parameters +dsn = cx_Oracle.makedsn( + host='', + port=1521, + service_name='_MEDIUM' +) + +try: + connection = cx_Oracle.connect( + user='SYS', + password='', + dsn=dsn, + mode=cx_Oracle.SYSDBA + ) + print("✓ Connected to Oracle Database") + + cursor = connection.cursor() + cursor.execute('SELECT banner FROM v$version WHERE ROWNUM = 1') + print(cursor.fetchone()) + + cursor.close() + connection.close() +except cx_Oracle.DatabaseError as e: + print(f"✗ Connection failed: {e}") +``` + +#### Option D: JDBC (Java) + +```java +// Connection string +String url = "jdbc:oracle:thin:@:1521/_MEDIUM"; +String user = "SYS"; +String password = ""; + +// JDBC connection +try { + Class.forName("oracle.jdbc.OracleDriver"); + Connection connection = DriverManager.getConnection(url, user, password); + System.out.println("✓ Connected to Oracle Database"); + + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT banner FROM v$version WHERE ROWNUM = 1"); + while (rs.next()) { + System.out.println(rs.getString(1)); + } + + rs.close(); + stmt.close(); + connection.close(); +} catch (SQLException e) { + System.err.println("✗ Connection failed: " + e.getMessage()); +} +``` + +## Data Migration + +After database provisioning, migrate your data to the new Exadata Database. + +### Migration Options + +1. **Oracle Data Pump Export/Import**: + ```bash + # Export from source database + expdp system/ full=Y directory=DPUMP_DIR \ + dumpfile=full_export_%U.dmp parallel=4 + + # Import to Exadata database + impdp system/ full=Y directory=DPUMP_DIR \ + dumpfile=full_export_%U.dmp parallel=4 + ``` + +2. **Oracle Zero Downtime Migration (ZDM)**: + - Automated migration tool + - Minimal/zero downtime + - Automatic cutover + - Recommended for production migrations + +3. **Oracle GoldenGate**: + - Real-time replication + - Continuous synchronization + - Planned or unplanned switchover + - For high-availability requirements + +## Monitoring and Management + +### CloudWatch Monitoring (AWS Console) + +1. **Access Metrics**: + - CloudWatch → Metrics → AWS/ODB + - Select VM cluster or database metrics + +2. **Key Metrics**: + - CPU Utilization + - Memory Usage + - Storage Space Used + - Network I/O + - Database Connections + +3. **Create Alarms**: + ```bash + aws cloudwatch put-metric-alarm \ + --alarm-name exadata-cpu-high \ + --alarm-description "Alert when CPU > 80%" \ + --metric-name CPUUtilization \ + --namespace AWS/ODB \ + --statistic Average \ + --period 300 \ + --threshold 80 \ + --comparison-operator GreaterThanThreshold + ``` + +### OCI Console Management + +From OCI Console, you can: +- Monitor database performance +- View backup status +- Manage database parameters +- Configure additional settings +- Update infrastructure settings + +## Troubleshooting + +### Common Issues + +**Issue**: Database creation fails with error +- **Solution**: Check VM cluster has sufficient storage and resources +- Verify administrator password meets complexity requirements +- Ensure database name is unique + +**Issue**: Cannot connect to database +- **Solution**: Verify SCAN listener is running +- Check network connectivity between VPC and ODB network +- Confirm security group rules allow port 1521 +- Test DNS resolution of SCAN hostname + +**Issue**: Backup failures +- **Solution**: Verify S3 bucket permissions and connectivity +- Check backup storage allocation in VM cluster +- Review backup logs in OCI Console + +## Best Practices + +1. **Infrastructure Sizing** + - Size for 80% peak load, not maximum + - Plan growth over 3-5 years + - Test with production-like data volumes + +2. **High Availability** + - Use Data Guard for standby protection + - Configure automatic backups + - Test restore procedures regularly + +3. **Security** + - Rotate SYS password regularly + - Use strong administrator credentials + - Enable Unified Auditing for compliance + - Secure SSH keys and wallets + +4. **Performance** + - Monitor metrics consistently + - Create appropriate indexes + - Partition large tables + - Use Oracle Database 23ai features (AI Vector Search, etc.) + +5. **Backup Strategy** + - Enable automatic backups + - Store backups in multiple locations + - Test recovery procedures + - Document Recovery Time Objective (RTO) + +## Next Steps + +1. **Load Data**: Migrate or load your application data +2. **Configure Applications**: Update connection strings and test +3. **Optimize Database**: Create indexes, tune queries +4. **Enable Monitoring**: Set up CloudWatch alerts +5. **Document Procedures**: Backup, recovery, maintenance runbooks +6. **Train Team**: Ensure operations team knows how to manage Exadata + +## Related Resources + +- [Oracle Exadata Database Service Overview](https://docs.oracle.com/en/engineered-systems/exadata-cloud-service/ecscm/exadata-cloud-infrastructure-overview.html) +- [Oracle Exadata Video Playlist](https://www.youtube.com/playlist?list=PLdtXkK5KBY55lKBR3SS3YrbfgxcgdC6ZT) +- [Oracle LiveLabs Exadata Workshop](https://apexapps.oracle.com/pls/apex/f?p=133:180:17374221011687::::wid:3311) +- [Oracle Zero Downtime Migration](https://www.oracle.com/database/zero-downtime-migration/) +- [AWS ODB Documentation](https://docs.aws.amazon.com/odb/) +- [OCI Exadata Documentation](https://docs.oracle.com/en/engineered-systems/exadata-cloud-service/) + +## Summary + +Provisioning Oracle Exadata Database Service on AWS involves four simple but comprehensive tasks: + +1. ✓ **Create ODB Network** - Private isolated network infrastructure (10-15 min) +2. ✓ **Create Exadata Infrastructure** - Database and storage servers (45-60 min) +3. ✓ **Create Exadata VM Cluster** - Virtual machines for databases (up to 6 hours) +4. ✓ **Create Oracle Database** - Actual database instance (15-30 min) + +**Total Provisioning Time**: Up to 7-8 hours from start to ready-for-use database + +Once provisioned, you have a production-grade Oracle Exadata infrastructure on AWS with low-latency access from your EC2 applications, enabling use of Oracle Database 23ai features including AI Vector Search for semantic similarity searches. + +For semantic similarity search implementation, refer to the [Oracle RAG Agents notebook](../rag-aiagent-chatbot/RAGChatbotwithAgentDevelopmentKit.ipynb) for code examples and best practices. diff --git a/notebooks/multicloud-oracledb-at-azure/images/01.png b/notebooks/multicloud-oracledb-at-azure/images/01.png new file mode 100644 index 00000000..1a033815 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/01.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/02.png b/notebooks/multicloud-oracledb-at-azure/images/02.png new file mode 100644 index 00000000..f0a93e23 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/02.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/03.png b/notebooks/multicloud-oracledb-at-azure/images/03.png new file mode 100644 index 00000000..f7c0696f Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/03.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/04.png b/notebooks/multicloud-oracledb-at-azure/images/04.png new file mode 100644 index 00000000..efcb75b1 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/04.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/05.png b/notebooks/multicloud-oracledb-at-azure/images/05.png new file mode 100644 index 00000000..6aebde80 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/05.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/06.png b/notebooks/multicloud-oracledb-at-azure/images/06.png new file mode 100644 index 00000000..73e1f555 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/06.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/07.png b/notebooks/multicloud-oracledb-at-azure/images/07.png new file mode 100644 index 00000000..192cdd93 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/07.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/08.png b/notebooks/multicloud-oracledb-at-azure/images/08.png new file mode 100644 index 00000000..70acc761 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/08.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/09.png b/notebooks/multicloud-oracledb-at-azure/images/09.png new file mode 100644 index 00000000..a33c5814 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/09.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/10.png b/notebooks/multicloud-oracledb-at-azure/images/10.png new file mode 100644 index 00000000..190748ad Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/10.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/11.png b/notebooks/multicloud-oracledb-at-azure/images/11.png new file mode 100644 index 00000000..f1f99caa Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/11.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/12.png b/notebooks/multicloud-oracledb-at-azure/images/12.png new file mode 100644 index 00000000..f1f99caa Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/12.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/13.png b/notebooks/multicloud-oracledb-at-azure/images/13.png new file mode 100644 index 00000000..fd45834c Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/13.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/14.png b/notebooks/multicloud-oracledb-at-azure/images/14.png new file mode 100644 index 00000000..6f0dd379 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/14.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/16.png b/notebooks/multicloud-oracledb-at-azure/images/16.png new file mode 100644 index 00000000..a6443544 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/16.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/17.png b/notebooks/multicloud-oracledb-at-azure/images/17.png new file mode 100644 index 00000000..60a02390 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/17.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/18.png b/notebooks/multicloud-oracledb-at-azure/images/18.png new file mode 100644 index 00000000..ee762bf4 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/18.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/19.png b/notebooks/multicloud-oracledb-at-azure/images/19.png new file mode 100644 index 00000000..1fa30c68 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/19.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/20.png b/notebooks/multicloud-oracledb-at-azure/images/20.png new file mode 100644 index 00000000..d2998dee Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/20.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/21.png b/notebooks/multicloud-oracledb-at-azure/images/21.png new file mode 100644 index 00000000..58921392 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/21.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/22.png b/notebooks/multicloud-oracledb-at-azure/images/22.png new file mode 100644 index 00000000..1e98f26a Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/22.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/23.png b/notebooks/multicloud-oracledb-at-azure/images/23.png new file mode 100644 index 00000000..74b080ef Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/23.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/24.png b/notebooks/multicloud-oracledb-at-azure/images/24.png new file mode 100644 index 00000000..b69dbf15 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/24.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/25.png b/notebooks/multicloud-oracledb-at-azure/images/25.png new file mode 100644 index 00000000..ef9ae12a Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/25.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/26.png b/notebooks/multicloud-oracledb-at-azure/images/26.png new file mode 100644 index 00000000..75ca60ea Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/26.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/27.png b/notebooks/multicloud-oracledb-at-azure/images/27.png new file mode 100644 index 00000000..beb5bfd2 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/27.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/28.png b/notebooks/multicloud-oracledb-at-azure/images/28.png new file mode 100644 index 00000000..bc351af4 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/28.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/29.png b/notebooks/multicloud-oracledb-at-azure/images/29.png new file mode 100644 index 00000000..45c47313 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/29.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/30.png b/notebooks/multicloud-oracledb-at-azure/images/30.png new file mode 100644 index 00000000..dc22d30e Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/30.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/31.png b/notebooks/multicloud-oracledb-at-azure/images/31.png new file mode 100644 index 00000000..cc950cab Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/31.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/32.png b/notebooks/multicloud-oracledb-at-azure/images/32.png new file mode 100644 index 00000000..650c7395 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/32.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/33.png b/notebooks/multicloud-oracledb-at-azure/images/33.png new file mode 100644 index 00000000..16e1f006 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/33.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/34.png b/notebooks/multicloud-oracledb-at-azure/images/34.png new file mode 100644 index 00000000..d8fce065 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/34.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/40.png b/notebooks/multicloud-oracledb-at-azure/images/40.png new file mode 100644 index 00000000..1e98f26a Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/40.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/41.png b/notebooks/multicloud-oracledb-at-azure/images/41.png new file mode 100644 index 00000000..74b080ef Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/41.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/42.png b/notebooks/multicloud-oracledb-at-azure/images/42.png new file mode 100644 index 00000000..b69dbf15 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/42.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/43.png b/notebooks/multicloud-oracledb-at-azure/images/43.png new file mode 100644 index 00000000..ef9ae12a Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/43.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/44.png b/notebooks/multicloud-oracledb-at-azure/images/44.png new file mode 100644 index 00000000..75ca60ea Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/44.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/45.png b/notebooks/multicloud-oracledb-at-azure/images/45.png new file mode 100644 index 00000000..beb5bfd2 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/45.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/46.png b/notebooks/multicloud-oracledb-at-azure/images/46.png new file mode 100644 index 00000000..bc351af4 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/46.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/images/47.png b/notebooks/multicloud-oracledb-at-azure/images/47.png new file mode 100644 index 00000000..45c47313 Binary files /dev/null and b/notebooks/multicloud-oracledb-at-azure/images/47.png differ diff --git a/notebooks/multicloud-oracledb-at-azure/oracle-azure-similarity-search.ipynb b/notebooks/multicloud-oracledb-at-azure/oracle-azure-similarity-search.ipynb new file mode 100644 index 00000000..82481af5 --- /dev/null +++ b/notebooks/multicloud-oracledb-at-azure/oracle-azure-similarity-search.ipynb @@ -0,0 +1,935 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "83b9653b", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "id": "478247c9", + "metadata": { + "id": "478247c9" + }, + "source": [ + "# Oracle Database Similarity Search on Azure\n", + "\n", + "This notebook demonstrates how to connect to an Oracle Autonomous Database hosted on Microsoft Azure and perform semantic similarity searches on text or numerical data using vector embeddings and similarity metrics.\n", + "\n", + "## Overview\n", + "\n", + "- **Cloud Platform**: Microsoft Azure\n", + "- **Database**: Oracle Autonomous Database@Azure\n", + "- **Embedding Model**: sentence-transformers for semantic embeddings\n", + "- **Similarity Metric**: Cosine similarity and Euclidean distance\n", + "- **Use Cases**: Document retrieval, product recommendations, semantic search, vector similarity\n", + "\n", + "## Prerequisites\n", + "\n", + "- Azure subscription with Oracle Database@Azure access\n", + "- Oracle Autonomous Database instance running on Azure\n", + "- Database connection details (endpoint, credentials)\n", + "- Google Colab environment (or local Jupyter Notebook)" + ] + }, + { + "cell_type": "markdown", + "id": "fa26d733", + "metadata": { + "id": "fa26d733" + }, + "source": [ + "## Section 1: Import Required Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67b5bf7e", + "metadata": { + "id": "67b5bf7e" + }, + "outputs": [], + "source": [ + "# Install required packages for Google Colab\n", + "import subprocess\n", + "import sys\n", + "\n", + "packages = [\n", + " 'cx_Oracle>=8.0',\n", + " 'azure-identity',\n", + " 'azure-keyvault-secrets',\n", + " 'pandas',\n", + " 'numpy',\n", + " 'scikit-learn',\n", + " 'sentence-transformers',\n", + " 'matplotlib',\n", + " 'seaborn'\n", + "]\n", + "\n", + "print(\"Installing required packages...\")\n", + "for package in packages:\n", + " print(f\" Installing {package}...\")\n", + " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", package])\n", + "\n", + "print(\"\\n✓ All packages installed successfully!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586160e0", + "metadata": { + "id": "586160e0" + }, + "outputs": [], + "source": [ + "# Import Required Libraries\n", + "import cx_Oracle\n", + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn.metrics.pairwise import cosine_similarity, euclidean_distances\n", + "from sklearn.preprocessing import normalize\n", + "from sentence_transformers import SentenceTransformer\n", + "import warnings\n", + "import json\n", + "from typing import Tuple, List, Optional\n", + "\n", + "# Azure libraries\n", + "try:\n", + " from azure.identity import DefaultAzureCredential, ClientSecretCredential\n", + " from azure.keyvault.secrets import SecretClient\n", + " AZURE_AVAILABLE = True\n", + "except ImportError:\n", + " AZURE_AVAILABLE = False\n", + " print(\"⚠ Azure libraries available but optional for local testing\")\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "\n", + "print(\"✓ Core libraries imported successfully!\")\n", + "if AZURE_AVAILABLE:\n", + " print(\"✓ Azure integration libraries loaded\")" + ] + }, + { + "cell_type": "markdown", + "id": "b774e1a9", + "metadata": { + "id": "b774e1a9" + }, + "source": [ + "## Interactive Search Function\n", + "\n", + "Use this cell to perform custom similarity searches on your data:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "942fb55d", + "metadata": { + "id": "942fb55d" + }, + "outputs": [], + "source": [ + "# Comprehensive Results Visualization and Analysis\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "if search_engine is not None and len(all_results) > 0:\n", + " # Configure plotting style\n", + " sns.set_style(\"whitegrid\")\n", + " plt.rcParams['figure.figsize'] = (12, 8)\n", + "\n", + " # Create subplots for each query\n", + " fig, axes = plt.subplots(1, len(all_results), figsize=(15, 5))\n", + " if len(all_results) == 1:\n", + " axes = [axes]\n", + "\n", + " for idx, result_set in enumerate(all_results):\n", + " query = result_set['query']\n", + " results = result_set['results']\n", + "\n", + " # Create bar chart for similarity scores\n", + " colors = plt.cm.RdYlGn(np.linspace(0.3, 0.9, len(results)))\n", + " bars = axes[idx].barh(range(len(results)), results['similarity_score'], color=colors)\n", + "\n", + " axes[idx].set_yticks(range(len(results)))\n", + " axes[idx].set_yticklabels([f\"Rank {i+1}\" for i in range(len(results))], fontsize=9)\n", + " axes[idx].set_xlabel('Similarity Score', fontsize=10)\n", + " axes[idx].set_title(f'Query {idx+1}: \"{query[:40]}...\"', fontsize=11, fontweight='bold')\n", + " axes[idx].set_xlim(0, 1)\n", + "\n", + " # Add value labels\n", + " for i, (bar, score) in enumerate(zip(bars, results['similarity_score'])):\n", + " axes[idx].text(score + 0.02, i, f'{score:.3f}', va='center', fontsize=9)\n", + "\n", + " plt.tight_layout()\n", + " plt.show()\n", + " print(\"✓ Visualization displayed above\\n\")\n", + "\n", + " # Summary statistics\n", + " print(\"=\"*80)\n", + " print(\"SEARCH ANALYTICS SUMMARY\")\n", + " print(\"=\"*80)\n", + " print(f\"Total documents indexed: {len(df)}\")\n", + " print(f\"Embedding dimension: {df['embeddings'].iloc[0].shape[0]}\")\n", + " print(f\"Total queries executed: {len(all_results)}\")\n", + "\n", + " # Aggregate statistics\n", + " all_scores = []\n", + " for result_set in all_results:\n", + " all_scores.extend(result_set['results']['similarity_score'].values)\n", + "\n", + " if all_scores:\n", + " print(f\"\\nSimilarity Score Statistics:\")\n", + " print(f\" Minimum: {min(all_scores):.4f}\")\n", + " print(f\" Maximum: {max(all_scores):.4f}\")\n", + " print(f\" Mean: {np.mean(all_scores):.4f}\")\n", + " print(f\" Median: {np.median(all_scores):.4f}\")\n", + " print(f\" Std Dev: {np.std(all_scores):.4f}\")\n", + "\n", + " print(\"=\"*80)\n", + "\n", + "else:\n", + " print(\"⚠ No results available for visualization\")" + ] + }, + { + "cell_type": "markdown", + "id": "16a145c3", + "metadata": { + "id": "16a145c3" + }, + "source": [ + "## Section 2: Configure Azure Credentials and Database Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78714e45", + "metadata": { + "id": "78714e45" + }, + "outputs": [], + "source": [ + "# Configure Azure Credentials and Database Connection\n", + "import os\n", + "from google.colab import userdata\n", + "\n", + "# Method 1: Retrieve credentials from Google Colab Secrets\n", + "try:\n", + " ORACLE_HOST = userdata.get('ORACLE_AZURE_HOST')\n", + " ORACLE_USER = userdata.get('ORACLE_AZURE_USER')\n", + " ORACLE_PASSWORD = userdata.get('ORACLE_AZURE_PASSWORD')\n", + " ORACLE_SERVICE = userdata.get('ORACLE_AZURE_SERVICE')\n", + " print(\"✓ Database credentials loaded from Colab Secrets\")\n", + "except:\n", + " # Method 2: Use environment variables or hardcoded values for testing\n", + " ORACLE_HOST = os.getenv('ORACLE_AZURE_HOST', 'your-oracle-db.oraclecloud.com')\n", + " ORACLE_USER = os.getenv('ORACLE_AZURE_USER', 'admin')\n", + " ORACLE_PASSWORD = os.getenv('ORACLE_AZURE_PASSWORD', 'your_password_here')\n", + " ORACLE_SERVICE = os.getenv('ORACLE_AZURE_SERVICE', 'ORCL')\n", + " print(\"⚠ Using default/environment variables for credentials\")\n", + "\n", + "# Oracle Database Configuration\n", + "DB_CONFIG = {\n", + " 'host': ORACLE_HOST,\n", + " 'port': 1521,\n", + " 'service_name': ORACLE_SERVICE,\n", + " 'user': ORACLE_USER,\n", + " 'password': ORACLE_PASSWORD,\n", + " 'wallet_location': None # Optional: path to wallet for mTLS\n", + "}\n", + "\n", + "# Optional: Azure Key Vault Integration (for secure credential storage)\n", + "def get_credentials_from_azure_keyvault(vault_url: str, secret_names: dict) -> dict:\n", + " \"\"\"\n", + " Retrieve database credentials from Azure Key Vault\n", + "\n", + " Args:\n", + " vault_url: Azure Key Vault URL\n", + " secret_names: Dict mapping config keys to secret names\n", + "\n", + " Returns:\n", + " Updated configuration dictionary\n", + " \"\"\"\n", + " if not AZURE_AVAILABLE:\n", + " print(\"⚠ Azure libraries not available\")\n", + " return {}\n", + "\n", + " try:\n", + " credential = DefaultAzureCredential()\n", + " client = SecretClient(vault_url=vault_url, credential=credential)\n", + "\n", + " credentials = {}\n", + " for key, secret_name in secret_names.items():\n", + " credentials[key] = client.get_secret(secret_name).value\n", + "\n", + " print(\"✓ Credentials retrieved from Azure Key Vault\")\n", + " return credentials\n", + " except Exception as e:\n", + " print(f\"⚠ Could not retrieve from Key Vault: {str(e)}\")\n", + " return {}\n", + "\n", + "# Display configuration (without exposing password)\n", + "print(\"\\n\" + \"=\"*60)\n", + "print(\"ORACLE DATABASE CONFIGURATION\")\n", + "print(\"=\"*60)\n", + "print(f\"Host: {DB_CONFIG['host']}\")\n", + "print(f\"Port: {DB_CONFIG['port']}\")\n", + "print(f\"Service Name: {DB_CONFIG['service_name']}\")\n", + "print(f\"Username: {DB_CONFIG['user']}\")\n", + "print(\"=\"*60)" + ] + }, + { + "cell_type": "markdown", + "id": "a453532f", + "metadata": { + "id": "a453532f" + }, + "source": [ + "## Section 3: Connect to Oracle Database on Azure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6ce7e06", + "metadata": { + "id": "d6ce7e06" + }, + "outputs": [], + "source": [ + "# Establish Oracle Database Connection\n", + "def create_oracle_connection(config: dict) -> Optional[cx_Oracle.Connection]:\n", + " \"\"\"\n", + " Create a connection to Oracle Autonomous Database on Azure\n", + "\n", + " Args:\n", + " config (dict): Database configuration\n", + "\n", + " Returns:\n", + " cx_Oracle.Connection or None: Database connection object\n", + " \"\"\"\n", + " try:\n", + " # Build connection string\n", + " if config.get('wallet_location'):\n", + " # Using wallet for mTLS (recommended for production)\n", + " dsn = cx_Oracle.makedsn(\n", + " config['host'],\n", + " config['port'],\n", + " service_name=config['service_name']\n", + " )\n", + " connection = cx_Oracle.connect(\n", + " user=config['user'],\n", + " password=config['password'],\n", + " dsn=dsn\n", + " )\n", + " else:\n", + " # Direct connection string\n", + " connection_string = (\n", + " f\"{config['user']}/{config['password']}@\"\n", + " f\"{config['host']}:{config['port']}/{config['service_name']}\"\n", + " )\n", + " connection = cx_Oracle.connect(connection_string)\n", + "\n", + " print(\"✓ Successfully connected to Oracle Autonomous Database on Azure\")\n", + " print(f\" Database Version: {connection.version}\")\n", + " return connection\n", + "\n", + " except cx_Oracle.DatabaseError as e:\n", + " error, = e.args\n", + " print(f\"✗ Database Connection Error: {error.message}\")\n", + " return None\n", + " except Exception as e:\n", + " print(f\"✗ Connection Error: {str(e)}\")\n", + " print(\"\\nTroubleshooting Tips:\")\n", + " print(\" 1. Verify database endpoint is correct\")\n", + " print(\" 2. Check network connectivity (firewall rules)\")\n", + " print(\" 3. Validate username and password\")\n", + " print(\" 4. Ensure Oracle Database@Azure is running\")\n", + " return None\n", + "\n", + "# Establish connection\n", + "oracle_conn = create_oracle_connection(DB_CONFIG)\n", + "\n", + "if oracle_conn:\n", + " print(\"\\n✓ Connection Status: ACTIVE\")\n", + "else:\n", + " print(\"\\n✗ Connection Status: FAILED\")\n", + " print(\"Please update credentials and retry connection\")" + ] + }, + { + "cell_type": "markdown", + "id": "c15f1651", + "metadata": { + "id": "c15f1651" + }, + "source": [ + "## Section 4: Load Data from Oracle Table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "125c8f00", + "metadata": { + "id": "125c8f00" + }, + "outputs": [], + "source": [ + "# Load Data from Oracle Database\n", + "def load_data_from_oracle(connection: cx_Oracle.Connection,\n", + " table_name: str,\n", + " columns: Optional[List[str]] = None,\n", + " limit: Optional[int] = None) -> Optional[pd.DataFrame]:\n", + " \"\"\"\n", + " Load data from an Oracle table into a pandas DataFrame\n", + "\n", + " Args:\n", + " connection: cx_Oracle connection object\n", + " table_name: Name of the table to query\n", + " columns: List of column names (None = all)\n", + " limit: Maximum number of rows to retrieve\n", + "\n", + " Returns:\n", + " pd.DataFrame: Data loaded from the table\n", + " \"\"\"\n", + " try:\n", + " if columns:\n", + " column_str = \", \".join(columns)\n", + " query = f\"SELECT {column_str} FROM {table_name}\"\n", + " else:\n", + " query = f\"SELECT * FROM {table_name}\"\n", + "\n", + " if limit:\n", + " query = f\"SELECT * FROM ({query}) WHERE ROWNUM <= {limit}\"\n", + "\n", + " # Read using pandas with Oracle connection\n", + " df = pd.read_sql(query, connection)\n", + "\n", + " print(f\"✓ Successfully loaded {len(df)} records from {table_name}\")\n", + " print(f\" Columns: {list(df.columns)}\")\n", + " print(f\" Data shape: {df.shape}\")\n", + " return df\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error loading data: {str(e)}\")\n", + " return None\n", + "\n", + "# Load sample data\n", + "if oracle_conn:\n", + " # Example: Load from a sample documents table\n", + " TABLE_NAME = 'DOCUMENTS' # Replace with your actual table name\n", + " COLUMNS = ['ID', 'TITLE', 'CONTENT', 'CATEGORY'] # Adjust columns as needed\n", + " LIMIT = 100 # Limit to first 100 records for demo\n", + "\n", + " print(\"Loading data from Oracle Database...\")\n", + " df = load_data_from_oracle(oracle_conn, TABLE_NAME, COLUMNS, LIMIT)\n", + "\n", + " if df is not None and not df.empty:\n", + " print(\"\\n\" + \"=\"*60)\n", + " print(\"DATA PREVIEW\")\n", + " print(\"=\"*60)\n", + " print(df.head())\n", + " print(f\"\\nData Info:\")\n", + " print(f\" Rows: {len(df)}\")\n", + " print(f\" Columns: {df.shape[1]}\")\n", + " print(f\" Memory Usage: {df.memory_usage(deep=True).sum() / 1024 / 1024:.2f} MB\")\n", + " else:\n", + " print(\"⚠ No data loaded or connection failed\")\n", + "else:\n", + " print(\"Cannot load data: Database connection not established\")" + ] + }, + { + "cell_type": "markdown", + "id": "4273a15f", + "metadata": { + "id": "4273a15f" + }, + "source": [ + "## Section 5: Prepare Data for Similarity Search" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0310a11", + "metadata": { + "id": "b0310a11" + }, + "outputs": [], + "source": [ + "# Load Embedding Model\n", + "print(\"Loading semantic embedding model...\")\n", + "print(\"(First run may take 1-2 minutes as model is downloaded)\")\n", + "\n", + "try:\n", + " embedding_model = SentenceTransformer('all-MiniLM-L6-v2')\n", + " print(\"✓ Embedding model loaded successfully\")\n", + " print(f\" Model: all-MiniLM-L6-v2\")\n", + " print(f\" Embedding dimension: 384\")\n", + "except Exception as e:\n", + " print(f\"✗ Error loading model: {str(e)}\")\n", + " embedding_model = None\n", + "\n", + "# Function to generate embeddings\n", + "def generate_embeddings(texts: List[str], batch_size: int = 32) -> np.ndarray:\n", + " \"\"\"\n", + " Generate semantic embeddings for text data\n", + "\n", + " Args:\n", + " texts: List of text strings\n", + " batch_size: Batch size for processing\n", + "\n", + " Returns:\n", + " np.ndarray: Embeddings array (n_samples, 384)\n", + " \"\"\"\n", + " print(f\"Generating embeddings for {len(texts)} documents...\")\n", + " embeddings = embedding_model.encode(texts, show_progress_bar=True, batch_size=batch_size)\n", + " print(f\"✓ Generated embeddings with shape: {embeddings.shape}\")\n", + " return embeddings\n", + "\n", + "# Prepare and embed data\n", + "if df is not None and embedding_model is not None and not df.empty:\n", + " # Identify text column\n", + " if 'CONTENT' in df.columns:\n", + " text_column = 'CONTENT'\n", + " elif 'TITLE' in df.columns:\n", + " text_column = 'TITLE'\n", + " else:\n", + " # Use first text column found\n", + " text_columns = df.select_dtypes(include=['object']).columns.tolist()\n", + " text_column = text_columns[0] if text_columns else None\n", + "\n", + " if text_column:\n", + " # Clean text data\n", + " df['text_clean'] = (df[text_column].fillna('').astype(str).str.strip()\n", + " .str.replace('\\n', ' ').str.replace('\\r', ' '))\n", + "\n", + " # Remove empty documents\n", + " df = df[df['text_clean'].str.len() > 0].reset_index(drop=True)\n", + "\n", + " print(f\"\\nPreparing text from column: '{text_column}'\")\n", + " print(f\"Documents with text: {len(df)}\")\n", + " print(f\"\\nSample text:\\n{df['text_clean'].iloc[0][:250]}...\\n\")\n", + "\n", + " # Generate embeddings\n", + " embeddings = generate_embeddings(df['text_clean'].tolist())\n", + "\n", + " # Store embeddings\n", + " df['embeddings'] = [embeddings[i] for i in range(len(df))]\n", + "\n", + " print(f\"\\n✓ Data preparation complete\")\n", + " print(f\" Total documents: {len(df)}\")\n", + " print(f\" Embedding dimension: {embeddings.shape[1]}\")\n", + " else:\n", + " print(\"⚠ No text column found for embedding\")\n", + " df = None\n", + "else:\n", + " print(\"⚠ Cannot prepare data: missing connection, model, or dataframe\")" + ] + }, + { + "cell_type": "markdown", + "id": "4a89f5c7", + "metadata": { + "id": "4a89f5c7" + }, + "source": [ + "## Section 6: Implement Similarity Search Algorithm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d9327cf", + "metadata": { + "id": "0d9327cf" + }, + "outputs": [], + "source": [ + "# Interactive similarity search function\n", + "def perform_custom_search(search_engine, query_text: str, top_k: int = 5, method: str = 'cosine'):\n", + " \"\"\"\n", + " Perform custom similarity search with detailed results display\n", + "\n", + " Args:\n", + " search_engine: OracleSimilaritySearch instance\n", + " query_text: Query string\n", + " top_k: Number of results to return\n", + " method: 'cosine' or 'euclidean'\n", + " \"\"\"\n", + " if search_engine is None:\n", + " print(\"⚠ Search engine not available\")\n", + " return None\n", + "\n", + " print(f\"\\n🔍 SIMILARITY SEARCH\")\n", + " print(f\"Query: '{query_text}'\")\n", + " print(f\"Method: {method.upper()}\")\n", + " print(f\"Top Results: {top_k}\")\n", + " print(\"-\"*80)\n", + "\n", + " # Execute search\n", + " if method == 'cosine':\n", + " results = search_engine.cosine_similarity_search(query_text, top_k=top_k)\n", + " score_col = 'similarity_score'\n", + " else:\n", + " results = search_engine.euclidean_distance_search(query_text, top_k=top_k)\n", + " score_col = 'distance'\n", + "\n", + " # Display results\n", + " for idx, row in results.iterrows():\n", + " print(f\"\\n✓ Rank {row['rank']}: {row[score_col]:.4f}\")\n", + "\n", + " # Show available metadata\n", + " for col in results.columns:\n", + " if col not in ['rank', 'similarity_score', 'distance', 'search_method']:\n", + " value = row[col]\n", + " if value is not None:\n", + " if isinstance(value, str) and len(str(value)) > 100:\n", + " print(f\" {col}: {str(value)[:100]}...\")\n", + " else:\n", + " print(f\" {col}: {value}\")\n", + "\n", + " print(\"\\n\" + \"=\"*80)\n", + " return results\n", + "\n", + "# Example: Custom search\n", + "if search_engine is not None:\n", + " # Modify this query to test\n", + " custom_query = \"Oracle database performance optimization on Azure\"\n", + " custom_results = perform_custom_search(\n", + " search_engine,\n", + " custom_query,\n", + " top_k=5,\n", + " method='cosine'\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "dfbd2ade", + "metadata": { + "id": "dfbd2ade" + }, + "source": [ + "## Section 7: Execute Similarity Search Query" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a91fe47", + "metadata": { + "id": "7a91fe47" + }, + "outputs": [], + "source": [ + "# Similarity Search Engine\n", + "class OracleSimilaritySearch:\n", + " \"\"\"\n", + " Semantic similarity search engine for Oracle Autonomous Database documents\n", + " \"\"\"\n", + "\n", + " def __init__(self, dataframe: pd.DataFrame, embedding_model):\n", + " \"\"\"\n", + " Initialize similarity search engine\n", + "\n", + " Args:\n", + " dataframe: DataFrame with embeddings column\n", + " embedding_model: Pre-trained embedding model\n", + " \"\"\"\n", + " self.df = dataframe.copy()\n", + " self.model = embedding_model\n", + " self.embeddings = np.array([emb for emb in dataframe['embeddings'].values])\n", + " print(f\"✓ Search engine initialized with {len(self.df)} documents\")\n", + "\n", + " def cosine_similarity_search(self, query: str, top_k: int = 5) -> pd.DataFrame:\n", + " \"\"\"\n", + " Find most similar documents using cosine similarity\n", + "\n", + " Args:\n", + " query: Query text\n", + " top_k: Number of results to return\n", + "\n", + " Returns:\n", + " pd.DataFrame: Top K results with similarity scores\n", + " \"\"\"\n", + " # Generate query embedding\n", + " query_embedding = self.model.encode([query])[0].reshape(1, -1)\n", + "\n", + " # Calculate cosine similarity\n", + " similarities = cosine_similarity(query_embedding, self.embeddings)[0]\n", + "\n", + " # Get top K results\n", + " top_indices = np.argsort(similarities)[::-1][:top_k]\n", + "\n", + " # Build results dataframe\n", + " results = self.df.iloc[top_indices].copy()\n", + " results['similarity_score'] = similarities[top_indices]\n", + " results['rank'] = range(1, len(results) + 1)\n", + " results['search_method'] = 'cosine_similarity'\n", + "\n", + " # Clean up columns for display\n", + " cols_to_drop = [col for col in results.columns if col in ['embeddings', 'text_clean']]\n", + " if cols_to_drop:\n", + " results = results.drop(columns=cols_to_drop)\n", + "\n", + " return results.reset_index(drop=True)\n", + "\n", + " def euclidean_distance_search(self, query: str, top_k: int = 5) -> pd.DataFrame:\n", + " \"\"\"\n", + " Find most similar documents using Euclidean distance\n", + "\n", + " Args:\n", + " query: Query text\n", + " top_k: Number of results to return\n", + "\n", + " Returns:\n", + " pd.DataFrame: Top K results with distance scores\n", + " \"\"\"\n", + " # Generate query embedding\n", + " query_embedding = self.model.encode([query])[0].reshape(1, -1)\n", + "\n", + " # Calculate Euclidean distance\n", + " distances = euclidean_distances(query_embedding, self.embeddings)[0]\n", + "\n", + " # Get top K results (smallest distances)\n", + " top_indices = np.argsort(distances)[:top_k]\n", + "\n", + " # Build results dataframe\n", + " results = self.df.iloc[top_indices].copy()\n", + " results['distance'] = distances[top_indices]\n", + " results['rank'] = range(1, len(results) + 1)\n", + " results['search_method'] = 'euclidean_distance'\n", + "\n", + " # Clean up columns\n", + " cols_to_drop = [col for col in results.columns if col in ['embeddings', 'text_clean']]\n", + " if cols_to_drop:\n", + " results = results.drop(columns=cols_to_drop)\n", + "\n", + " return results.reset_index(drop=True)\n", + "\n", + " def hybrid_search(self, query: str, top_k: int = 5, method: str = 'cosine') -> pd.DataFrame:\n", + " \"\"\"\n", + " Perform hybrid search combining multiple similarity metrics\n", + "\n", + " Args:\n", + " query: Query text\n", + " top_k: Number of results to return\n", + " method: 'cosine' or 'euclidean'\n", + "\n", + " Returns:\n", + " pd.DataFrame: Top K hybrid search results\n", + " \"\"\"\n", + " if method == 'cosine':\n", + " return self.cosine_similarity_search(query, top_k)\n", + " else:\n", + " return self.euclidean_distance_search(query, top_k)\n", + "\n", + "# Initialize search engine\n", + "if df is not None and 'embeddings' in df.columns and embedding_model is not None:\n", + " search_engine = OracleSimilaritySearch(df, embedding_model)\n", + "else:\n", + " search_engine = None\n", + " print(\"⚠ Cannot initialize search engine\")" + ] + }, + { + "cell_type": "markdown", + "id": "adaa291e", + "metadata": { + "id": "adaa291e" + }, + "source": [ + "## Section 8: Display and Analyze Results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ee8ce31", + "metadata": { + "id": "2ee8ce31" + }, + "outputs": [], + "source": [ + "# Execute Similarity Search Queries\n", + "if search_engine is not None:\n", + " # Define example queries\n", + " example_queries = [\n", + " \"artificial intelligence and machine learning\",\n", + " \"cloud database solutions and Azure services\",\n", + " \"data analytics and business intelligence\"\n", + " ]\n", + "\n", + " print(\"=\"*80)\n", + " print(\"EXECUTING SIMILARITY SEARCH QUERIES\")\n", + " print(\"=\"*80)\n", + "\n", + " all_results = []\n", + "\n", + " for idx, query in enumerate(example_queries, 1):\n", + " print(f\"\\n📋 Query {idx}: '{query}'\")\n", + " print(\"-\"*80)\n", + "\n", + " # Perform cosine similarity search\n", + " results = search_engine.cosine_similarity_search(query, top_k=3)\n", + " all_results.append({\n", + " 'query': query,\n", + " 'results': results\n", + " })\n", + "\n", + " # Display results\n", + " for result_idx, row in results.iterrows():\n", + " print(f\"\\n Rank {row['rank']}: Score = {row['similarity_score']:.4f}\")\n", + "\n", + " # Display relevant columns\n", + " for col in results.columns:\n", + " if col not in ['rank', 'similarity_score', 'search_method']:\n", + " value = row[col]\n", + " if isinstance(value, str) and len(value) > 80:\n", + " print(f\" {col}: {value[:80]}...\")\n", + " else:\n", + " print(f\" {col}: {value}\")\n", + "\n", + " print(\"\\n\" + \"=\"*80)\n", + "else:\n", + " print(\"⚠ Search engine not initialized. Cannot execute queries.\")" + ] + }, + { + "cell_type": "markdown", + "id": "3844c181", + "metadata": { + "id": "3844c181" + }, + "source": [ + "## Cleanup and Best Practices" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "237c7d8f", + "metadata": { + "id": "237c7d8f" + }, + "outputs": [], + "source": [ + "# Close database connection\n", + "if oracle_conn:\n", + " try:\n", + " oracle_conn.close()\n", + " print(\"✓ Oracle database connection closed\")\n", + " except Exception as e:\n", + " print(f\"⚠ Warning closing connection: {str(e)}\")\n", + "\n", + "print(\"\\n\" + \"=\"*80)\n", + "print(\"BEST PRACTICES FOR PRODUCTION IMPLEMENTATION\")\n", + "print(\"=\"*80)\n", + "\n", + "best_practices = \"\"\"\n", + "## Security Best Practices\n", + "✓ Use Azure Key Vault for credential management\n", + "✓ Enable mTLS (mutual TLS) with wallet files\n", + "✓ Implement firewall rules and network security groups\n", + "✓ Use managed identities instead of service principals\n", + "✓ Enable SQL audit logging and monitoring\n", + "✓ Apply row-level security (RLS) in Oracle\n", + "\n", + "## Performance Optimization\n", + "✓ Cache embeddings in database (e.g., VECTOR datatype in Oracle 23c+)\n", + "✓ Use connection pooling (cx_Oracle.SessionPool)\n", + "✓ Batch process queries for large datasets\n", + "✓ Implement approximate nearest neighbor (ANN) search\n", + "✓ Create indexes on vector columns\n", + "✓ Monitor query execution time and optimize queries\n", + "\n", + "## Scalability Considerations\n", + "✓ Use dedicated vector databases for large-scale applications:\n", + " - Azure Cognitive Search\n", + " - Pinecone\n", + " - Milvus\n", + " - Weaviate\n", + "✓ Implement pagination for large result sets\n", + "✓ Use async queries for non-blocking operations\n", + "✓ Implement rate limiting and caching layers\n", + "\n", + "## Data Management\n", + "✓ Regularly update embeddings for new/modified documents\n", + "✓ Version embedding models (track which model created each embedding)\n", + "✓ Archive old embeddings periodically\n", + "✓ Maintain consistency between database and embedding cache\n", + "✓ Implement data validation and quality checks\n", + "\n", + "## Monitoring & Logging\n", + "✓ Track query latency and response times\n", + "✓ Monitor database connection health\n", + "✓ Log embedding generation times\n", + "✓ Monitor similarity score distributions\n", + "✓ Set up alerts for performance degradation\n", + "✓ Use Azure Monitor for infrastructure metrics\n", + "\n", + "## Testing & Validation\n", + "✓ Test with representative data samples\n", + "✓ Validate embedding quality and consistency\n", + "✓ Benchmark different similarity algorithms\n", + "✓ Test error scenarios (connection failures, timeouts)\n", + "✓ Load test with realistic query patterns\n", + "\"\"\"\n", + "\n", + "print(best_practices)\n", + "print(\"=\"*80)" + ] + }, + { + "cell_type": "markdown", + "id": "0a4a5f69", + "metadata": { + "id": "0a4a5f69" + }, + "source": [ + "## References and Additional Resources\n", + "\n", + "### Official Documentation\n", + "- [Oracle Autonomous Database on Azure](https://www.oracle.com/cloud/azure/)\n", + "- [cx_Oracle Python Library](https://cx-oracle.readthedocs.io/)\n", + "- [Sentence Transformers](https://www.sbert.net/)\n", + "- [Azure Oracle Database@Azure Docs](https://docs.oracle.com/en-us/iaas/Content/Database/Concepts/oracledatabaseathome.htm)\n", + "\n", + "### Useful Tutorials\n", + "- Oracle Database@Azure Setup: [Oracle-Azure Documentation](https://github.com/oracle-devrel/oracle-autonomous-database-samples)\n", + "- Vector Search Implementation: [Semantic Search Guide](https://huggingface.co/blog/semantic-search-blog)\n", + "\n", + "### Related Technologies\n", + "- Azure Cognitive Search for vector search at scale\n", + "- Oracle Vector Search (Oracle 23c, 26ai)\n", + "- Open-source vector databases: Milvus, Weaviate, Pinecone" + ] + } + ], + "metadata": { + "colab": { + "include_colab_link": true, + "provenance": [] + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/multicloud-oracledb-at-azure/oracle-db-azure.md b/notebooks/multicloud-oracledb-at-azure/oracle-db-azure.md new file mode 100644 index 00000000..74ab41bc --- /dev/null +++ b/notebooks/multicloud-oracledb-at-azure/oracle-db-azure.md @@ -0,0 +1,503 @@ +## Introduction + +This guide provides comprehensive instructions for deploying an **Oracle Autonomous Database on Microsoft Azure**. We cover both the Azure Portal UI and the Azure Command Line Interface (CLI) approaches, enabling you to choose the method that best fits your workflow. + +### What You'll Learn + +- Authenticate and navigate the Azure Portal +- Create and configure Azure infrastructure components (Resource Groups, Virtual Networks, Subnets) +- Deploy Oracle Autonomous Database instances on Azure +- Set up secure access through Azure Bastion and Windows VMs +- Troubleshoot common deployment issues +- Access your database from various applications (.NET, Java, Python, and more) + +### Prerequisites + +- Active Microsoft Azure subscription +- Oracle Database@Azure service access in your region +- Azure CLI installed (for CLI-based approaches) +- Basic familiarity with cloud networking concepts + +### Table of Contents + +1. [Azure Portal Authentication](#azure-portal-authentication) +2. [Creating Oracle Database via Azure Portal](#creating-oracle-database-via-azure-portal) +3. [Azure CLI Setup](#azure-cli-setup) +4. [Infrastructure Setup with Azure CLI](#infrastructure-setup-with-azure-cli) +5. [Creating an Azure Resource Group](#creating-an-azure-resource-group) +6. [Creating a Virtual Network and Subnet](#creating-a-virtual-network-and-subnet) +7. [Creating Azure Public IP](#creating-azure-public-ip) +8. [Creating Azure Bastion for Secure Access](#creating-azure-bastion-for-secure-access) +9. [Creating a Windows Virtual Machine](#creating-a-windows-virtual-machine) +10. [Accessing the Windows VM via RDP](#accessing-the-windows-vm-via-rdp) +11. [Deploying Oracle Autonomous Database via Azure CLI](#deploying-oracle-autonomous-database-via-azure-cli) + +--- + +## Azure Portal Authentication + +To authenticate into the Azure Portal, navigate to [https://portal.azure.com/](https://portal.azure.com/) and sign in with your Azure credentials. + +![Oracle Multicloud@Azure](images/01.png) +![Oracle Multicloud@Azure](images/02.png) +![Oracle Multicloud@Azure](images/03.png) +![Oracle Multicloud@Azure](images/04.png) + +## Creating Oracle Database via Azure Portal + +### Step 1: Search for Oracle Database@Azure + +In the Azure Portal search bar, type "Oracle Database@Azure" to locate the service. + +![Oracle Multicloud@Azure](images/05.png) + +### Step 2: Select Service Type + +Choose from the available options: +- **Oracle Autonomous Database Service** - For managed database workloads +- **Oracle Exadata Database** - For high-performance enterprise deployments + +Click on **Oracle Autonomous Database Service** in the left navigation. + +![Oracle Multicloud@Azure](images/06.png) + +### Step 3: Initiate Database Creation + +Click the **+ Create** button to begin the database configuration wizard. + +![Oracle Multicloud@Azure](images/07.png) + +### Step 4: Configure Subscription and Resource Group + +- Select your Azure subscription +- Choose an existing resource group or create a new one + +![Oracle Multicloud@Azure](images/08.png) + +### Step 5: Configure Database Settings + +Configure the following parameters: + +- **Workload Type**: Choose one of: + - Data Warehouse (OLAP workloads) + - Transaction Processing (OLTP workloads) + - JSON (Document-oriented workloads) + - APEX (Application Express) + +- **Database Version**: Select latest Database version available +- **Admin Password**: Set a strong administrator password +- **Backup Retention**: Configure backup retention period (in days) + +![Oracle Multicloud@Azure](images/09.png) + +### Step 6: Configure Network Access + +Select your network access strategy: + +- **Private Network Only**: Restrict access to your virtual network (recommended for production) +- **Public Network**: Allow access from configured IP ranges + +![Oracle Multicloud@Azure](images/10.png) + +![Oracle Multicloud@Azure](images/11.png) + +### Step 7: Review and Create + +Review all configuration details on the **Review + Create** screen. + +![Oracle Multicloud@Azure](images/13.png) + +Click **Create** to initiate the deployment process. + +### Step 8: Monitor Deployment + +The deployment process will proceed through the following states: +- **Provisioning**: Resources are being created +- **Available**: Database is ready for use +- **Failed**: Review error logs if deployment fails + +![Oracle Multicloud@Azure](images/14.png) +![Oracle Multicloud@Azure](images/15.png) + +--- + +## Azure CLI Setup + +### Installation + +The Azure CLI is a cross-platform command-line tool available for Windows, macOS, and Linux. + +**For macOS:** +```bash +brew update && brew install azure-cli +``` + +**For Linux (Ubuntu/Debian):** +```bash +sudo apt-get update +sudo apt-get install azure-cli +``` + +For other operating systems, see [Azure CLI Installation Guide](https://learn.microsoft.com/en-us/cli/azure/). + +### Authentication + +Log in to Azure: + +```bash +az login +``` + +Your default web browser will open to authenticate. After authentication, you'll see your subscriptions and tenant information in the terminal: + +``` +A web browser has opened at https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize. +Please continue the login in the web browser. If no web browser is available or if the web browser +fails to open, use device code flow with `az login --use-device-code`. + +Retrieving tenants and subscriptions for the selection... +``` + +### List Your Subscriptions + +Verify your subscriptions and account details: + +```bash +az account list +``` + +Example response: + +```json +[ + { + "cloudName": "AzureCloud", + "homeTenantId": "e63de8-XXXX-XXX0863da5d", + "id": "99d4fb0e-XXXX-XXXXdb9025", + "isDefault": true, + "managedByTenants": [], + "name": "My Subscription", + "state": "Enabled", + "tenantDefaultDomain": "example.onmicrosoft.com", + "tenantDisplayName": "Example Tenant", + "tenantId": "e63de867-XXXX-e90c0863da5d", + "user": { + "name": "user@example.com", + "type": "user" + } + } +] +``` + +--- + +## Infrastructure Setup with Azure CLI + +To deploy Oracle Autonomous Database@Azure via CLI, you'll need the following prerequisites: + +- **Resource Group**: Container for all related resources +- **Virtual Network (VNet)**: Private network for your resources +- **Delegated Subnet**: Subnet delegated to `Oracle.Database/networkAttachments` service +- **Public IP** (optional): For external access +- **Bastion Host** (optional): Secure jump host for private resource access + +--- + +## Creating an Azure Resource Group + +An Azure Resource Group is a logical container that holds related resources for an Azure solution. It enables you to manage resources as a single unit. + +### Create Resource Group + +```bash +az group create \ + --name \ + --location +``` + +**Example:** + +```bash +az group create \ + --name oracle-adb-rg \ + --location eastus +``` + +**Expected Output:** + +```json +{ + "id": "/subscriptions/99d4fb0e-XXX-XXX-XX025/resourceGroups/oracle-adb-rg", + "location": "eastus", + "managedBy": null, + "name": "oracle-adb-rg", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +Verify your resource group on the [Azure Portal](https://portal.azure.com/): + +![Oracle Multicloud@Azure](images/40.png) + +--- + +## Creating a Virtual Network and Subnet + +### What is Azure Virtual Network? + +Azure Virtual Network (VNet) provides the foundational building block for your private network in Azure. It enables secure communication between: +- Azure resources (Virtual Machines, databases, etc.) +- The internet +- On-premises networks + +### Create VNet and Subnet + +```bash +az network vnet create \ + --name \ + --resource-group \ + --address-prefix 10.0.0.0/16 \ + --subnet-name \ + --subnet-prefixes 10.0.0.0/24 +``` + +**Example:** + +```bash +az network vnet create \ + --name oracle-vnet-1 \ + --resource-group oracle-adb-rg \ + --address-prefix 10.0.0.0/16 \ + --subnet-name oracle-subnet-1 \ + --subnet-prefixes 10.0.0.0/24 +``` + +### Verify on Azure Portal + +Search for "Virtual Networks" and view your newly created VNet: + +![Oracle Multicloud@Azure](images/41.png) + +Navigate to **Subnets** to verify the subnet configuration: + +![Oracle Multicloud@Azure](images/42.png) + +Click the edit icon to view detailed subnet settings: + +![Oracle Multicloud@Azure](images/43.png) + +--- + +## Creating Azure Public IP + +### What is Azure Public IP? + +An Azure Public IP address provides inbound and outbound connectivity for Azure resources. It's essential for resources that need to communicate over the internet. + +### Create Public IP + +```bash +az network public-ip create \ + --resource-group \ + --name \ + --sku Standard \ + --location \ + --zone 1 2 3 +``` + +**Example:** + +```bash +az network public-ip create \ + --resource-group oracle-adb-rg \ + --name oracle-public-ip \ + --sku Standard \ + --location eastus \ + --zone 1 2 3 +``` + +**Expected Output:** + +![Oracle Multicloud@Azure](images/44.png) + +--- + +## Creating Azure Bastion for Secure Access + +### What is Azure Bastion? + +Azure Bastion provides secure and seamless SSH/RDP access to virtual machines over the internet using TLS, eliminating the need for: +- Public IP addresses on VMs +- VPN clients +- Exposed RDP/SSH ports + +### Create Bastion Subnet + +First, create a dedicated subnet for Azure Bastion: + +```bash +az network vnet subnet create \ + --name AzureBastionSubnet \ + --resource-group \ + --vnet-name \ + --address-prefix 10.0.1.0/26 +``` + +**Example:** + +```bash +az network vnet subnet create \ + --name AzureBastionSubnet \ + --resource-group oracle-adb-rg \ + --vnet-name oracle-vnet-1 \ + --address-prefix 10.0.1.0/26 +``` + +### Create Bastion Host + +```bash +az network bastion create \ + --name oracle-bastion \ + --public-ip-address \ + --resource-group \ + --vnet-name \ + --location +``` + +Verify on Azure Portal: + +![Oracle Multicloud@Azure](images/45.png) + +--- + +## Creating a Windows Virtual Machine + +### List Available VM Images + +To view available VM images: + +```bash +az vm image list +``` + +This returns a JSON array of available images, including: + +```json +{ + "architecture": "x64", + "offer": "WindowsServer", + "publisher": "MicrosoftWindowsServer", + "sku": "2022-Datacenter", + "urn": "MicrosoftWindowsServer:WindowsServer:2022-Datacenter:latest", + "urnAlias": "Win2022Datacenter", + "version": "latest" +}, +{ + "architecture": "x64", + "offer": "UbuntuServer", + "publisher": "Canonical", + "sku": "22_04-lts-gen2", + "urn": "Canonical:0001-com-ubuntu-server-jammy:22_04-lts-gen2:latest", + "urnAlias": "Ubuntu2204", + "version": "latest" +} +``` + +### Create Windows VM + +```bash +az vm create \ + --name \ + --resource-group \ + --public-ip-address \ + --image Win2022Datacenter \ + --admin-username azureuser +``` + +**Example:** + +```bash +az vm create \ + --name oracle-windows-vm \ + --resource-group oracle-adb-rg \ + --public-ip-address oracle-public-ip \ + --image Win2022Datacenter \ + --admin-username azureuser +``` + +### Create Linux/Ubuntu VM + +```bash +az vm create \ + --name \ + --resource-group \ + --image Ubuntu2204 \ + --admin-username azureuser +``` + +Verify on Azure Portal: + +![Oracle Multicloud@Azure](images/46.png) + +--- + +## Accessing the Windows VM via RDP + +### Download RDP Configuration + +1. Navigate to your VM in the Azure Portal +2. Click the **Connect** button at the top +3. Download the RDP configuration file + +![Oracle Multicloud@Azure](images/47.png) + +### Connect Using RDP Client + +Use any RDP client or [Microsoft Windows App](https://learn.microsoft.com/en-us/windows-app/overview) to open the downloaded RDP file. + +Once connected, you can: +- Open a web browser to access external websites +- Connect to your Oracle Database using SQL*Plus or SQL Developer +- Configure network settings as needed + +--- + +## Deploying Oracle Autonomous Database via Azure CLI + +For a complete example of deploying Oracle Autonomous Database@Azure using Azure CLI, see the [Oracle Autonomous Database Azure CLI Samples](https://github.com/oracle-devrel/oracle-autonomous-database-samples/tree/main/multicloud/azure-cli) repository. + +### Key Configuration Parameters + +When creating your database instance via CLI, consider: +- **Workload Optimization**: Choose the appropriate workload type +- **Network Configuration**: Use delegated subnets for secure access +- **Backup Strategy**: Configure retention policies +- **High Availability**: Enable availability domain redundancy where supported + +--- + +## Next Steps + +After successfully deploying your Oracle Autonomous Database on Azure, you can: + +- **Connect from Various Applications**: .NET Core, Java, Python, SQL*Plus, and more +- **Set Up Data Studio**: Use Oracle's web-based IDE +- **Configure Backups**: Implement automated backup strategies +- **Monitor Performance**: Use Azure Monitor and Oracle Cloud Tools +- **Scale Resources**: Adjust compute and storage as needed + +For additional resources and troubleshooting, refer to the [Oracle Database@Azure Documentation](https://docs.oracle.com/). + +--- + +## Additional Resources + +- [Azure CLI Documentation](https://learn.microsoft.com/en-us/cli/azure/) +- [Oracle Database@Azure](https://www.oracle.com/cloud/azure/) +- [Oracle Autonomous Database Samples](https://github.com/oracle-devrel/oracle-autonomous-database-samples) +- [Azure Virtual Network Documentation](https://learn.microsoft.com/en-us/azure/virtual-network/) +- [Azure Bastion Documentation](https://learn.microsoft.com/en-us/azure/bastion/) \ No newline at end of file diff --git a/notebooks/rag-aiagent-chatbot/RAGChatbotwithAgentDevelopmentKit.ipynb b/notebooks/rag-aiagent-chatbot/RAGChatbotwithAgentDevelopmentKit.ipynb new file mode 100644 index 00000000..5b4d23d5 --- /dev/null +++ b/notebooks/rag-aiagent-chatbot/RAGChatbotwithAgentDevelopmentKit.ipynb @@ -0,0 +1,2471 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "authorship_tag": "ABX9TyNaloVX3ORxHo7kAlolC1Gr", + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Install the required packages" + ], + "metadata": { + "id": "FOeF9DZVzG8F" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KqvtkNtwfmI9" + }, + "outputs": [], + "source": [ + "!pip install \"oci[adk]\" oci langchain-oracledb langchain" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Save the OCI config file in Google Drive. the file format should be as shown below\n", + "```\n", + "[DEFAULT]\n", + "\n", + "user=ocid1.user.oc1..aaaaaaaarvvpvxiwapsybxoefovyd7wpl4q\n", + "\n", + "fingerprint=ca:bf:b7:53:57:78:9b:ff:bb:47:3e:ae:a2:a2:b8:03\n", + "\n", + "tenancy=ocid1.tenancy.oc1..aaaaaaaaj4kymyo4xwxyv3gfa\n", + "\n", + "region=us-phoenix-1\n", + "\n", + "key_file=/root/.oci/yourkeyfile.pem\n", + "```\n" + ], + "metadata": { + "id": "CVlWaEOjzQAF" + } + }, + { + "cell_type": "code", + "source": [ + "# Create OCI configuration directory if it doesn't exist\n", + "import os\n", + "oci_dir = '/root/.oci'\n", + "if not os.path.exists(oci_dir):\n", + " os.makedirs(oci_dir)\n", + "\n", + "# Mount Google Drive to access OCI configuration files stored in the cloud\n", + "from google.colab import drive\n", + "drive.mount('/content/drive')" + ], + "metadata": { + "id": "ofxha7XQkDld", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "36a356de-dcd1-4d8f-b960-9104f19f64f5" + }, + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mounted at /content/drive\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Copy OCI authentication files from Google Drive to the Colab environment\n", + "# This includes the OCI config file with credentials and API key\n", + "!cp /content/drive/MyDrive/OCI_KEY/config ~/.oci/config\n", + "# Copy private key file required for OCI API authentication\n", + "!cp /content/drive/MyDrive/OCI_KEY/yourtenancy_sso.pem $oci_dir/yourtenancy_sso.pem" + ], + "metadata": { + "id": "GAObd07Livl7" + }, + "execution_count": 3, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Import required libraries\n", + "import json\n", + "import os\n", + "from typing import Dict, List, Optional, Any\n", + "from datetime import datetime\n", + "# Oracle ADK for building AI agents\n", + "from oci.addons.adk import Agent, AgentClient, tool\n", + "# OCI Object Storage client for managing cloud storage\n", + "from oci.object_storage import ObjectStorageClient\n", + "# OCI SDK for cloud infrastructure interaction\n", + "import oci\n", + "# Asynchronous programming support\n", + "import asyncio\n", + "import nest_asyncio" + ], + "metadata": { + "id": "fV8PBC3cGvaa" + }, + "execution_count": 4, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# ============================================================================\n", + "# DOCUMENT STORE - In-memory knowledge base for RAG\n", + "# ============================================================================\n", + "\n", + "class DocumentStore:\n", + " \"\"\"\n", + " Simple in-memory document store for RAG (Retrieval-Augmented Generation).\n", + " Stores documents that can be retrieved and used to augment agent responses.\n", + " \"\"\"\n", + "\n", + " def __init__(self):\n", + " \"\"\"Initialize the document store with sample documents about OCI and RAG.\"\"\"\n", + " self.documents = {\n", + " \"oracle_cloud_overview\": {\n", + " \"title\": \"Oracle Cloud Infrastructure Overview\",\n", + " \"content\": \"\"\"Oracle Cloud Infrastructure (OCI) is a secure, economical and high-performing\n", + " cloud platform. OCI provides a complete suite of cloud computing services including compute,\n", + " storage, networking, database, analytics, and AI services.\"\"\"\n", + " },\n", + " \"adk_intro\": {\n", + " \"title\": \"Oracle Agent Development Toolkit\",\n", + " \"content\": \"\"\"The Oracle Agent Development Toolkit (ADK) enables developers to create\n", + " intelligent agents that can use tools, reason about tasks, and interact with various services.\n", + " Agents can be deployed as endpoints and invoked via API.\"\"\"\n", + " },\n", + " \"rag_basics\": {\n", + " \"title\": \"RAG (Retrieval-Augmented Generation) Basics\",\n", + " \"content\": \"\"\"RAG combines document retrieval with generative AI. When a user asks a question,\n", + " the system retrieves relevant documents from a knowledge base and uses them to generate\n", + " accurate, contextual responses.\"\"\"\n", + " }\n", + " }\n", + "\n", + " def search_documents(self, query: str) -> List[Dict[str, str]]:\n", + " \"\"\"\n", + " Search documents using simple keyword matching.\n", + "\n", + " Args:\n", + " query (str): Search query to find relevant documents\n", + "\n", + " Returns:\n", + " List[Dict[str, str]]: List of matching documents with preview text\n", + " \"\"\"\n", + " query_lower = query.lower()\n", + " results = []\n", + "\n", + " # Iterate through all documents and find matches\n", + " for doc_id, doc in self.documents.items():\n", + " # Check if query matches in title or content\n", + " title_match = query_lower in doc[\"title\"].lower()\n", + " content_match = query_lower in doc[\"content\"].lower()\n", + "\n", + " # Add to results if either title or content matches\n", + " if title_match or content_match:\n", + " results.append({\n", + " \"id\": doc_id,\n", + " \"title\": doc[\"title\"],\n", + " \"preview\": doc[\"content\"][:200] + \"...\"\n", + " })\n", + "\n", + " return results[:3] # Return top 3 results\n", + "\n", + "\n", + "# Global document store instance - used by tool functions\n", + "doc_store = DocumentStore()\n", + "\n", + "\n", + "# ============================================================================\n", + "# TOOL DEFINITIONS FOR THE AGENT\n", + "# These functions are exposed to the agent as tools it can call to perform tasks\n", + "# ============================================================================\n", + "\n", + "@tool\n", + "def retrieve_documents(query: str) -> Dict[str, Any]:\n", + " \"\"\"\n", + " Retrieve relevant documents from the knowledge base based on the query.\n", + " This tool allows the agent to search for and retrieve documents.\n", + "\n", + " Args:\n", + " query(str): The search query to find relevant documents\n", + "\n", + " Returns:\n", + " dict: Retrieved documents with metadata, status, and timestamp\n", + " \"\"\"\n", + " results = doc_store.search_documents(query)\n", + "\n", + " # Return error if no documents found\n", + " if not results:\n", + " return {\n", + " \"status\": \"not_found\",\n", + " \"message\": f\"No documents found for query: '{query}'\",\n", + " \"documents\": []\n", + " }\n", + "\n", + " # Return successful results with timestamp\n", + " return {\n", + " \"status\": \"success\",\n", + " \"message\": f\"Found {len(results)} relevant document(s)\",\n", + " \"documents\": results,\n", + " \"timestamp\": datetime.now().isoformat()\n", + " }\n", + "\n", + "\n", + "@tool\n", + "def get_document_content(document_id: str) -> Dict[str, Any]:\n", + " \"\"\"\n", + " Retrieve the full content of a specific document by its ID.\n", + " This tool allows the agent to access complete document text.\n", + "\n", + " Args:\n", + " document_id(str): The ID of the document to retrieve\n", + "\n", + " Returns:\n", + " dict: Full document content with metadata or error message\n", + " \"\"\"\n", + " # Check if document exists in store\n", + " if document_id not in doc_store.documents:\n", + " return {\n", + " \"status\": \"error\",\n", + " \"message\": f\"Document not found: {document_id}\"\n", + " }\n", + "\n", + " # Return complete document with metadata\n", + " doc = doc_store.documents[document_id]\n", + " return {\n", + " \"status\": \"success\",\n", + " \"id\": document_id,\n", + " \"title\": doc[\"title\"],\n", + " \"content\": doc[\"content\"],\n", + " \"retrieved_at\": datetime.now().isoformat()\n", + " }\n", + "\n", + "\n", + "@tool\n", + "def search_oci_knowledge_base(topic: str, category: str = \"all\") -> Dict[str, Any]:\n", + " \"\"\"\n", + " Search the OCI knowledge base for information about specific topics.\n", + " The agent can use this to find OCI-related information across categories.\n", + "\n", + " Args:\n", + " topic(str): The topic to search for (e.g., \"storage\", \"compute\")\n", + " category(str): Category filter (all, compute, storage, database, ai)\n", + "\n", + " Returns:\n", + " dict: Search results with relevant OCI information and count\n", + " \"\"\"\n", + " # OCI knowledge base organized by service category\n", + " knowledge_base = {\n", + " \"compute\": [\n", + " \"VM instances\", \"Container instances\", \"Functions\",\n", + " \"Kubernetes Engine\", \"Bare Metal Compute\"\n", + " ],\n", + " \"storage\": [\n", + " \"Object Storage\", \"Block Volumes\", \"File Storage\",\n", + " \"Archive Storage\", \"Data Transfer\"\n", + " ],\n", + " \"database\": [\n", + " \"Autonomous Database\", \"MySQL Database\", \"PostgreSQL Database\",\n", + " \"NoSQL Database\", \"Database Backup\"\n", + " ],\n", + " \"ai\": [\n", + " \"AI Services\", \"Generative AI\", \"Document Understanding\",\n", + " \"Anomaly Detection\", \"Data Science\"\n", + " ]\n", + " }\n", + "\n", + " results = []\n", + " topic_lower = topic.lower()\n", + "\n", + " # Determine which categories to search\n", + " if category == \"all\":\n", + " categories = knowledge_base.keys()\n", + " else:\n", + " categories = [category] if category in knowledge_base else []\n", + "\n", + " # Search for topic matches across selected categories\n", + " for cat in categories:\n", + " for item in knowledge_base[cat]:\n", + " if topic_lower in item.lower():\n", + " results.append({\"category\": cat, \"item\": item})\n", + "\n", + " # Return results with metadata\n", + " return {\n", + " \"topic\": topic,\n", + " \"category\": category,\n", + " \"results\": results,\n", + " \"count\": len(results)\n", + " }\n", + "\n", + "\n", + "@tool\n", + "def get_conversation_context(session_id: str) -> Dict[str, Any]:\n", + " \"\"\"\n", + " Retrieve conversation context information for maintaining conversation state.\n", + " This helps the agent understand the current conversation session.\n", + "\n", + " Args:\n", + " session_id(str): The session identifier\n", + "\n", + " Returns:\n", + " dict: Conversation context with session info, topics, and status\n", + " \"\"\"\n", + " # Return context structure with session information\n", + " return {\n", + " \"session_id\": session_id,\n", + " \"start_time\": datetime.now().isoformat(),\n", + " \"context\": {\n", + " \"user_intent\": \"general_inquiry\",\n", + " \"topics_discussed\": [],\n", + " \"documents_referenced\": []\n", + " },\n", + " \"status\": \"active\"\n", + " }\n", + "\n", + "\n", + "# ============================================================================\n", + "# RAG CHATBOT CLASS - Main conversational interface\n", + "# ============================================================================\n", + "\n", + "class RAGChatbot:\n", + " \"\"\"\n", + " RAG-enabled conversational AI chatbot using Oracle ADK.\n", + " Combines document retrieval with generative AI to provide accurate, context-aware responses.\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " agent_endpoint_id: str,\n", + " auth_type: str = \"api_key\",\n", + " profile: str = \"DEFAULT\",\n", + " region: str = \"us-chicago-1\"\n", + " ):\n", + " \"\"\"\n", + " Initialize the RAG chatbot with OCI authentication and configuration.\n", + "\n", + " Args:\n", + " agent_endpoint_id (str): The OCID of the deployed agent endpoint\n", + " auth_type (str): Authentication type (api_key, instance_principal, resource_principal)\n", + " profile (str): OCI config profile name to use for authentication\n", + " region (str): OCI region where the agent endpoint is deployed\n", + " \"\"\"\n", + " self.agent_endpoint_id = agent_endpoint_id\n", + " self.auth_type = auth_type\n", + " self.profile = profile\n", + " self.region = region\n", + " # Store conversation history for context and logging\n", + " self.conversation_history = []\n", + " self.agent = None\n", + " self.client = None\n", + "\n", + " # Initialize the OCI agent client connection\n", + " self._initialize_client()\n", + "\n", + " def _initialize_client(self):\n", + " \"\"\"\n", + " Initialize the OCI Agent Client for communication with the agent endpoint.\n", + " Handles authentication and connection setup.\n", + " \"\"\"\n", + " try:\n", + " # Create agent client with specified authentication and region\n", + " self.client = AgentClient(\n", + " auth_type=self.auth_type,\n", + " profile=self.profile,\n", + " region=self.region\n", + " )\n", + " print(f\"✓ Agent client initialized\")\n", + " print(f\" - Region: {self.region}\")\n", + " print(f\" - Auth Type: {self.auth_type}\")\n", + " except Exception as e:\n", + " print(f\"✗ Failed to initialize agent client: {e}\")\n", + " raise\n", + "\n", + " def setup_agent(self, instructions: str = None):\n", + " \"\"\"\n", + " Set up the agent with instructions and register available tools.\n", + " The agent uses these tools to answer user queries.\n", + "\n", + " Args:\n", + " instructions (str): Custom system instructions for the agent behavior.\n", + " If None, uses default RAG instructions.\n", + " \"\"\"\n", + " # Use default instructions if not provided\n", + " if instructions is None:\n", + " instructions = \"\"\"You are a helpful RAG-powered assistant for Oracle Cloud Infrastructure.\n", + " Your role is to:\n", + " 1. Answer questions about OCI services and features\n", + " 2. Retrieve relevant documents from the knowledge base when needed\n", + " 3. Provide accurate, context-aware responses based on retrieved information\n", + " 4. Guide users through OCI concepts and best practices\n", + "\n", + " When answering questions:\n", + " - Always search for relevant documents first\n", + " - Cite the documents you used\n", + " - Provide clear, structured answers\n", + " - Ask clarifying questions if needed\"\"\"\n", + "\n", + " try:\n", + " # Create agent with client, endpoint, instructions, and tools\n", + " self.agent = Agent(\n", + " client=self.client,\n", + " agent_endpoint_id=self.agent_endpoint_id,\n", + " instructions=instructions,\n", + " # Register all available tools for the agent to use\n", + " tools=[\n", + " retrieve_documents,\n", + " get_document_content,\n", + " search_oci_knowledge_base,\n", + " get_conversation_context\n", + " ]\n", + " )\n", + "\n", + " # Sync instructions and tools to remote agent endpoint\n", + " self.agent.setup()\n", + " print(\"✓ Agent setup complete with RAG tools\")\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Failed to setup agent: {e}\")\n", + " raise\n", + "\n", + " def chat(self, user_input: str) -> Dict[str, Any]:\n", + " \"\"\"\n", + " Process a user message and generate a response using the agent.\n", + " The agent will use available tools (RAG, search, etc.) to formulate responses.\n", + "\n", + " Args:\n", + " user_input (str): The user's message or query\n", + "\n", + " Returns:\n", + " dict: Response object with agent output, status, and metadata\n", + " \"\"\"\n", + " # Check if agent is initialized before processing\n", + " if not self.agent:\n", + " return {\n", + " \"status\": \"error\",\n", + " \"message\": \"Agent not initialized. Call setup_agent() first.\"\n", + " }\n", + "\n", + " try:\n", + " # Record user message in conversation history with timestamp\n", + " self.conversation_history.append({\n", + " \"role\": \"user\",\n", + " \"content\": user_input,\n", + " \"timestamp\": datetime.now().isoformat()\n", + " })\n", + "\n", + " print(f\"\\n📝 User: {user_input}\")\n", + " print(\"-\" * 60)\n", + "\n", + " # Execute the agent to process the user input and generate response\n", + " response = self.agent.run(user_input)\n", + "\n", + " # Record agent response in conversation history\n", + " response_text = str(response)\n", + " self.conversation_history.append({\n", + " \"role\": \"assistant\",\n", + " \"content\": response_text,\n", + " \"timestamp\": datetime.now().isoformat()\n", + " })\n", + "\n", + " # Return successful response with metadata\n", + " return {\n", + " \"status\": \"success\",\n", + " \"user_input\": user_input,\n", + " \"response\": response,\n", + " \"turn\": len(self.conversation_history) // 2\n", + " }\n", + "\n", + " except Exception as e:\n", + " print(f\"✗ Error processing message: {e}\")\n", + " return {\n", + " \"status\": \"error\",\n", + " \"message\": str(e),\n", + " \"user_input\": user_input\n", + " }\n", + "\n", + " def get_conversation_history(self) -> List[Dict[str, Any]]:\n", + " \"\"\"\n", + " Retrieve the complete conversation history.\n", + "\n", + " Returns:\n", + " List[Dict[str, Any]]: List of all user and assistant messages with timestamps\n", + " \"\"\"\n", + " return self.conversation_history\n", + "\n", + " def clear_history(self):\n", + " \"\"\"Clear the conversation history for starting a fresh session.\"\"\"\n", + " self.conversation_history = []\n", + " print(\"✓ Conversation history cleared\")\n", + "" + ], + "metadata": { + "id": "S0QK286YGxTJ" + }, + "execution_count": 5, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# ============================================================================\n", + "# EXAMPLE USAGE - Demonstration functions\n", + "# ============================================================================\n", + "\n", + "def main():\n", + " \"\"\"\n", + " Demonstrate the RAG chatbot functionality with sample queries.\n", + " This function shows how to initialize, configure, and use the chatbot.\n", + " \"\"\"\n", + " print(\"=\" * 70)\n", + " print(\"Oracle ADK RAG Chatbot Demo\")\n", + " print(\"=\" * 70)\n", + "\n", + " # Configuration parameters - Replace with your actual values\n", + " # Get the agent endpoint ID from your Oracle ADK deployment\n", + " AGENT_ENDPOINT_ID = \"ocid1.genaiagentendpoint.oc1.us-chicago-1.amaaaaaaknuwtjiadfdoluzq45s4owu7wvvwgtdtwvsau7h4ef75edqbmu5a\"\n", + " # OCI region where the agent is deployed\n", + " REGION = \"us-chicago-1\"\n", + " # Authentication method (api_key, instance_principal, resource_principal)\n", + " AUTH_TYPE = \"api_key\"\n", + " # OCI configuration profile to use from ~/.oci/config\n", + " PROFILE = \"DEFAULT_CHICAGO\"\n", + "\n", + " # Step 1: Initialize the chatbot with OCI configuration\n", + " print(\"\\n1. Initializing RAG Chatbot...\")\n", + " print(\"-\" * 70)\n", + "\n", + " try:\n", + " chatbot = RAGChatbot(\n", + " agent_endpoint_id=AGENT_ENDPOINT_ID,\n", + " auth_type=AUTH_TYPE,\n", + " profile=PROFILE,\n", + " region=REGION\n", + " )\n", + "\n", + " # Step 2: Setup the agent with RAG tools and instructions\n", + " print(\"\\n2. Setting up Agent with RAG Tools...\")\n", + " print(\"-\" * 70)\n", + " chatbot.setup_agent()\n", + "\n", + " # Step 3: Run sample queries through the chatbot\n", + " print(\"\\n3. Conducting Conversations...\")\n", + " print(\"-\" * 70)\n", + "\n", + " # Sample queries to demonstrate the chatbot's capabilities\n", + " sample_queries = [\n", + " \"What is Database Sharding?\",\n", + " \"How does RAG work?\",\n", + " \"Tell me about OCI Database services\"\n", + " ]\n", + "\n", + " # Process each sample query\n", + " for query in sample_queries:\n", + " response = chatbot.chat(query)\n", + "\n", + " # Display response or error\n", + " if response[\"status\"] == \"success\":\n", + " print(f\"\\n🤖 Agent Response:\")\n", + " response[\"response\"].pretty_print()\n", + " else:\n", + " print(f\"\\n❌ Error: {response['message']}\")\n", + "\n", + " # Step 4: Display summary of conversation history\n", + " print(\"\\n4. Conversation History Summary\")\n", + " print(\"-\" * 70)\n", + " history = chatbot.get_conversation_history()\n", + " print(f\"Total exchanges: {len(history) // 2}\")\n", + " for i, msg in enumerate(history):\n", + " role_icon = \"👤\" if msg[\"role\"] == \"user\" else \"🤖\"\n", + " print(f\"{role_icon} {msg['role'].upper()}: {msg['content'][:60]}...\")\n", + "\n", + " except Exception as e:\n", + " print(f\"\\n❌ Failed to run chatbot: {e}\")\n", + " print(\"\\nTroubleshooting:\")\n", + " print(\"1. Ensure your OCI config is set up: ~/.oci/config\")\n", + " print(\"2. Update AGENT_ENDPOINT_ID with your actual endpoint OCID\")\n", + " print(\"3. Verify your OCI region is correct\")\n", + " print(\"4. Check your authentication credentials\")\n", + "\n", + "\n", + "def interactive_chat():\n", + " \"\"\"\n", + " Run an interactive chatbot session allowing user input until exit.\n", + " Provides a command-line interface for conversing with the RAG chatbot.\n", + " \"\"\"\n", + " print(\"=\" * 70)\n", + " print(\"Interactive RAG Chatbot\")\n", + " print(\"=\" * 70)\n", + " print(\"Type 'exit' to quit, 'history' to see conversation history\")\n", + " print()\n", + "\n", + " # Agent endpoint ID - Replace with your actual endpoint\n", + " AGENT_ENDPOINT_ID = 'ocid1.genaiagentendpoint.oc1.us-chicago-1.amaaaaaaknuwtjiadfdoluzq45s4owu7wvvwgtdtwvsau7h4ef75edqbmu5a'\n", + "\n", + " try:\n", + " # Initialize chatbot with configuration\n", + " chatbot = RAGChatbot(\n", + " agent_endpoint_id=AGENT_ENDPOINT_ID,\n", + " auth_type=\"api_key\",\n", + " profile=\"DEFAULT_CHICAGO\",\n", + " region=\"us-chicago-1\"\n", + " )\n", + "\n", + " # Setup agent with tools\n", + " chatbot.setup_agent()\n", + "\n", + " # Interactive loop - continue until user exits\n", + " while True:\n", + " # Get user input from command line\n", + " user_input = input(\"\\n👤 You: \").strip()\n", + "\n", + " # Skip empty inputs\n", + " if not user_input:\n", + " continue\n", + "\n", + " # Exit the chatbot\n", + " if user_input.lower() == \"exit\":\n", + " print(\"\\n👋 Goodbye!\")\n", + " break\n", + "\n", + " # Display conversation history\n", + " if user_input.lower() == \"history\":\n", + " history = chatbot.get_conversation_history()\n", + " print(\"\\n📋 Conversation History:\")\n", + " for msg in history:\n", + " print(f\" {msg['role']}: {msg['content'][:50]}...\")\n", + " continue\n", + "\n", + " # Process user query and display response\n", + " response = chatbot.chat(user_input)\n", + " if response[\"status\"] == \"success\":\n", + " print(f\"\\n🤖 Assistant: {response['response']}\")\n", + " else:\n", + " print(f\"\\n❌ Error: {response['message']}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"\\n❌ Chatbot error: {e}\")\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " # Apply nest_asyncio to allow nested event loops, which is common in environments like Colab\n", + " #nest_asyncio.apply()\n", + " # Run the demo with sample queries\n", + " main()\n", + "\n", + " # Uncomment the line below to run interactive chat instead\n", + " #interactive_chat()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "IVjR9Yrgyw-1", + "outputId": "9568335c-e634-4265-b83a-10e762f5b6c9" + }, + "execution_count": 10, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "======================================================================\n", + "Oracle ADK RAG Chatbot Demo\n", + "======================================================================\n", + "\n", + "1. Initializing RAG Chatbot...\n", + "----------------------------------------------------------------------\n", + "✓ Agent client initialized\n", + " - Region: us-chicago-1\n", + " - Auth Type: api_key\n", + "\n", + "2. Setting up Agent with RAG Tools...\n", + "----------------------------------------------------------------------\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[2;36m[01/28/26 13:51:41] \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Checking integrity of agent details\u001b[33m...\u001b[0m \n" + ], + "text/html": [ + "
[01/28/26 13:51:41]  INFO     Checking integrity of agent details...                                               \n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[2;36m[01/28/26 13:51:42] \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Checking synchronization of local and remote agent settings\u001b[33m...\u001b[0m \n" + ], + "text/html": [ + "
[01/28/26 13:51:42]  INFO     Checking synchronization of local and remote agent settings...                       \n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[2;36m[01/28/26 13:51:43] \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Checking synchronization of local and remote function tools\u001b[33m...\u001b[0m \n" + ], + "text/html": [ + "
[01/28/26 13:51:43]  INFO     Checking synchronization of local and remote function tools...                       \n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m────────────────────────────────\u001b[0m\u001b[34m Local and remote function tools found \u001b[0m\u001b[34m────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m Local function tools (4): \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m['get_conversation_context', 'get_document_content', 'retrieve_documents', 'search_oci_knowledge_base']\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m Remote function tools (4): \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36m['get_conversation_context', 'get_document_content', 'retrieve_documents', 'search_oci_knowledge_base']\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭───────────────────────────────── Local and remote function tools found ─────────────────────────────────╮\n",
+              " Local function tools (4):                                                                               \n",
+              " ['get_conversation_context', 'get_document_content', 'retrieve_documents', 'search_oci_knowledge_base'] \n",
+              "                                                                                                         \n",
+              " Remote function tools (4):                                                                              \n",
+              " ['get_conversation_context', 'get_document_content', 'retrieve_documents', 'search_oci_knowledge_base'] \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Checking synchronization of local and remote RAG tools\u001b[33m...\u001b[0m \n" + ], + "text/html": [ + "
                     INFO     Checking synchronization of local and remote RAG tools...                            \n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Checking synchronization of local and remote SQL tools\u001b[33m...\u001b[0m \n" + ], + "text/html": [ + "
                     INFO     Checking synchronization of local and remote SQL tools...                            \n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m Local and remote SQL tools found \u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m Local SQL tools (0): \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m[]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m Remote SQL tools (0): \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36m[]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Local and remote SQL tools found ─╮\n",
+              " Local SQL tools (0):               \n",
+              " []                                 \n",
+              "                                    \n",
+              " Remote SQL tools (0):              \n",
+              " []                                 \n",
+              "╰────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "✓ Agent setup complete with RAG tools\n", + "\n", + "3. Conducting Conversations...\n", + "----------------------------------------------------------------------\n", + "\n", + "📝 User: What is Database Sharding?\n", + "------------------------------------------------------------\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m─────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mWhat is Database Sharding?\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaal47qpbpftqdso6ah6ojdyniejd6kcl7yrn5q2fqbbabq\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭───────────────────────────────── Chat request to remote agent: None ──────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                    \n",
+              "                                                                                                       \n",
+              " user message:                                                                                         \n",
+              " What is Database Sharding?                                                                            \n",
+              "                                                                                                       \n",
+              " performed actions by client:                                                                          \n",
+              " []                                                                                                    \n",
+              "                                                                                                       \n",
+              " session id:                                                                                           \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaal47qpbpftqdso6ah6ojdyniejd6kcl7yrn5q2fqbbabq \n",
+              "╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"action_id\": \"beb7c5db-7880-43cf-8b71-ba58bba9d803\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"function_call\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"name\": \"retrieve_documents\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"arguments\": \"{\\\"query\\\": \\\"Database Sharding\\\"}\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────── Chat response from remote agent ──────────────────╮\n",
+              " (Local <-- Remote)                                                  \n",
+              "                                                                     \n",
+              " agent message:                                                      \n",
+              " null                                                                \n",
+              "                                                                     \n",
+              " required actions for client to take:                                \n",
+              " [                                                                   \n",
+              "     {                                                               \n",
+              "         \"action_id\": \"beb7c5db-7880-43cf-8b71-ba58bba9d803\",        \n",
+              "         \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\", \n",
+              "         \"function_call\": {                                          \n",
+              "             \"name\": \"retrieve_documents\",                           \n",
+              "             \"arguments\": \"{\\\"query\\\": \\\"Database Sharding\\\"}\"       \n",
+              "         }                                                           \n",
+              "     }                                                               \n",
+              " ]                                                                   \n",
+              "                                                                     \n",
+              " guardrail result:                                                   \n",
+              " None                                                                \n",
+              "                                                                     \n",
+              "                                                                     \n",
+              "╰─────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m Function call requested by agent and mapped local handler function \u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool call arguments: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'query': 'Database Sharding'}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Mapped local handler function name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Function call requested by agent and mapped local handler function ─╮\n",
+              " Agent function tool name:                                            \n",
+              " retrieve_documents                                                   \n",
+              "                                                                      \n",
+              " Agent function tool call arguments:                                  \n",
+              " {'query': 'Database Sharding'}                                       \n",
+              "                                                                      \n",
+              " Mapped local handler function name:                                  \n",
+              " retrieve_documents                                                   \n",
+              "╰──────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m───────────────────────────────\u001b[0m\u001b[38;5;173m Obtained local function execution result \u001b[0m\u001b[38;5;173m───────────────────────────────\u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'status': 'not_found', 'message': \"No documents found for query: 'Database Sharding'\", 'documents': []}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭──────────────────────────────── Obtained local function execution result ────────────────────────────────╮\n",
+              " {'status': 'not_found', 'message': \"No documents found for query: 'Database Sharding'\", 'documents': []} \n",
+              "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m──────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[{'action_id': 'beb7c5db-7880-43cf-8b71-ba58bba9d803', 'performed_action_type': \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mfound for query: \\'Database Sharding\\'\", \"documents\": []}'}]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaal47qpbpftqdso6ah6ojdyniejd6kcl7yrn5q2fqbbabq\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────── Chat request to remote agent: None ───────────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " user message:                                                                                                   \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " performed actions by client:                                                                                    \n",
+              " [{'action_id': 'beb7c5db-7880-43cf-8b71-ba58bba9d803', 'performed_action_type':                                 \n",
+              " 'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents  \n",
+              " found for query: \\'Database Sharding\\'\", \"documents\": []}'}]                                                    \n",
+              "                                                                                                                 \n",
+              " session id:                                                                                                     \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaal47qpbpftqdso6ah6ojdyniejd6kcl7yrn5q2fqbbabq           \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m────────────────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"action_id\": \"840746df-77bc-481d-8733-81fbac0f0e70\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"function_call\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"name\": \"search_oci_knowledge_base\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"arguments\": \"{\\\"topic\\\": \\\"Database Sharding\\\", \\\"category\\\": \\\"database\\\"}\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰───────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭───────────────────────────── Chat response from remote agent ─────────────────────────────╮\n",
+              " (Local <-- Remote)                                                                        \n",
+              "                                                                                           \n",
+              " agent message:                                                                            \n",
+              " null                                                                                      \n",
+              "                                                                                           \n",
+              " required actions for client to take:                                                      \n",
+              " [                                                                                         \n",
+              "     {                                                                                     \n",
+              "         \"action_id\": \"840746df-77bc-481d-8733-81fbac0f0e70\",                              \n",
+              "         \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",                       \n",
+              "         \"function_call\": {                                                                \n",
+              "             \"name\": \"search_oci_knowledge_base\",                                          \n",
+              "             \"arguments\": \"{\\\"topic\\\": \\\"Database Sharding\\\", \\\"category\\\": \\\"database\\\"}\" \n",
+              "         }                                                                                 \n",
+              "     }                                                                                     \n",
+              " ]                                                                                         \n",
+              "                                                                                           \n",
+              " guardrail result:                                                                         \n",
+              " None                                                                                      \n",
+              "                                                                                           \n",
+              "                                                                                           \n",
+              "╰───────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m Function call requested by agent and mapped local handler function \u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36msearch_oci_knowledge_base\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool call arguments: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'topic': 'Database Sharding', 'category': 'database'}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Mapped local handler function name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36msearch_oci_knowledge_base\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Function call requested by agent and mapped local handler function ─╮\n",
+              " Agent function tool name:                                            \n",
+              " search_oci_knowledge_base                                            \n",
+              "                                                                      \n",
+              " Agent function tool call arguments:                                  \n",
+              " {'topic': 'Database Sharding', 'category': 'database'}               \n",
+              "                                                                      \n",
+              " Mapped local handler function name:                                  \n",
+              " search_oci_knowledge_base                                            \n",
+              "╰──────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m───────────────────\u001b[0m\u001b[38;5;173m Obtained local function execution result \u001b[0m\u001b[38;5;173m────────────────────\u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'topic': 'Database Sharding', 'category': 'database', 'results': [], 'count': 0}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰───────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭──────────────────── Obtained local function execution result ─────────────────────╮\n",
+              " {'topic': 'Database Sharding', 'category': 'database', 'results': [], 'count': 0} \n",
+              "╰───────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m──────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[{'action_id': '840746df-77bc-481d-8733-81fbac0f0e70', 'performed_action_type': \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"topic\": \"Database Sharding\", \"category\": \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m\"database\", \"results\": [], \"count\": 0}'}]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaal47qpbpftqdso6ah6ojdyniejd6kcl7yrn5q2fqbbabq\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────── Chat request to remote agent: None ───────────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " user message:                                                                                                   \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " performed actions by client:                                                                                    \n",
+              " [{'action_id': '840746df-77bc-481d-8733-81fbac0f0e70', 'performed_action_type':                                 \n",
+              " 'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"topic\": \"Database Sharding\", \"category\":        \n",
+              " \"database\", \"results\": [], \"count\": 0}'}]                                                                       \n",
+              "                                                                                                                 \n",
+              " session id:                                                                                                     \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaal47qpbpftqdso6ah6ojdyniejd6kcl7yrn5q2fqbbabq           \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m───────────────────────────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m───────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m{\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"role\": \"AGENT\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"content\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"text\": \"Database sharding is a technique used in distributed databases to horizontally partition data \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32macross multiple physical databases, known as shards, that share no hardware or software. This approach allows \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mfor the distribution of data across multiple databases, making it possible to scale out and improve the \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mperformance of the database. In Oracle Globally Distributed Database, sharding enables the automatic \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mdistribution and replication of data across a pool of Oracle databases. The sharding process involves dividing \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mthe data into smaller, more manageable pieces, called shards, and distributing them across multiple databases, \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mallowing for improved scalability, performance, and availability, as well as reduced storage costs.\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"citations\": null,\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"paragraph_citations\": null\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m },\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"time_created\": \"2026-01-28T13:52:15.259000+00:00\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m}\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭──────────────────────────────────────── Chat response from remote agent ────────────────────────────────────────╮\n",
+              " (Local <-- Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " agent message:                                                                                                  \n",
+              " {                                                                                                               \n",
+              "     \"role\": \"AGENT\",                                                                                            \n",
+              "     \"content\": {                                                                                                \n",
+              "         \"text\": \"Database sharding is a technique used in distributed databases to horizontally partition data  \n",
+              " across multiple physical databases, known as shards, that share no hardware or software. This approach allows   \n",
+              " for the distribution of data across multiple databases, making it possible to scale out and improve the         \n",
+              " performance of the database. In Oracle Globally Distributed Database, sharding enables the automatic            \n",
+              " distribution and replication of data across a pool of Oracle databases. The sharding process involves dividing  \n",
+              " the data into smaller, more manageable pieces, called shards, and distributing them across multiple databases,  \n",
+              " allowing for improved scalability, performance, and availability, as well as reduced storage costs.\",           \n",
+              "         \"citations\": null,                                                                                      \n",
+              "         \"paragraph_citations\": null                                                                             \n",
+              "     },                                                                                                          \n",
+              "     \"time_created\": \"2026-01-28T13:52:15.259000+00:00\"                                                          \n",
+              " }                                                                                                               \n",
+              "                                                                                                                 \n",
+              " required actions for client to take:                                                                            \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " guardrail result:                                                                                               \n",
+              " None                                                                                                            \n",
+              "                                                                                                                 \n",
+              "                                                                                                                 \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "🤖 Agent Response:\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────────────\u001b[0m\u001b[34m Agent run response \u001b[0m\u001b[34m──────────────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m agent text message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mDatabase sharding is a technique used in distributed databases to horizontally partition data across multiple \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mphysical databases, known as shards, that share no hardware or software. This approach allows for the \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mdistribution of data across multiple databases, making it possible to scale out and improve the performance of \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mthe database. In Oracle Globally Distributed Database, sharding enables the automatic distribution and \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mreplication of data across a pool of Oracle databases. The sharding process involves dividing the data into \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32msmaller, more manageable pieces, called shards, and distributing them across multiple databases, allowing for \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mimproved scalability, performance, and availability, as well as reduced storage costs.\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────────────── Agent run response ───────────────────────────────────────────────╮\n",
+              " agent text message:                                                                                             \n",
+              " Database sharding is a technique used in distributed databases to horizontally partition data across multiple   \n",
+              " physical databases, known as shards, that share no hardware or software. This approach allows for the           \n",
+              " distribution of data across multiple databases, making it possible to scale out and improve the performance of  \n",
+              " the database. In Oracle Globally Distributed Database, sharding enables the automatic distribution and          \n",
+              " replication of data across a pool of Oracle databases. The sharding process involves dividing the data into     \n",
+              " smaller, more manageable pieces, called shards, and distributing them across multiple databases, allowing for   \n",
+              " improved scalability, performance, and availability, as well as reduced storage costs.                          \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "📝 User: How does RAG work?\n", + "------------------------------------------------------------\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m─────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mHow does RAG work?\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaafgqsft6h2c2zcphmd3ljne454upzegolf3owdudlcaxa\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭───────────────────────────────── Chat request to remote agent: None ──────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                    \n",
+              "                                                                                                       \n",
+              " user message:                                                                                         \n",
+              " How does RAG work?                                                                                    \n",
+              "                                                                                                       \n",
+              " performed actions by client:                                                                          \n",
+              " []                                                                                                    \n",
+              "                                                                                                       \n",
+              " session id:                                                                                           \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaafgqsft6h2c2zcphmd3ljne454upzegolf3owdudlcaxa \n",
+              "╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"action_id\": \"de6e59b8-0345-47e5-9a7c-a7713ea5cc89\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"function_call\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"name\": \"retrieve_documents\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"arguments\": \"{\\\"query\\\": \\\"RAG workflow\\\"}\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────── Chat response from remote agent ──────────────────╮\n",
+              " (Local <-- Remote)                                                  \n",
+              "                                                                     \n",
+              " agent message:                                                      \n",
+              " null                                                                \n",
+              "                                                                     \n",
+              " required actions for client to take:                                \n",
+              " [                                                                   \n",
+              "     {                                                               \n",
+              "         \"action_id\": \"de6e59b8-0345-47e5-9a7c-a7713ea5cc89\",        \n",
+              "         \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\", \n",
+              "         \"function_call\": {                                          \n",
+              "             \"name\": \"retrieve_documents\",                           \n",
+              "             \"arguments\": \"{\\\"query\\\": \\\"RAG workflow\\\"}\"            \n",
+              "         }                                                           \n",
+              "     }                                                               \n",
+              " ]                                                                   \n",
+              "                                                                     \n",
+              " guardrail result:                                                   \n",
+              " None                                                                \n",
+              "                                                                     \n",
+              "                                                                     \n",
+              "╰─────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m Function call requested by agent and mapped local handler function \u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool call arguments: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'query': 'RAG workflow'}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Mapped local handler function name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Function call requested by agent and mapped local handler function ─╮\n",
+              " Agent function tool name:                                            \n",
+              " retrieve_documents                                                   \n",
+              "                                                                      \n",
+              " Agent function tool call arguments:                                  \n",
+              " {'query': 'RAG workflow'}                                            \n",
+              "                                                                      \n",
+              " Mapped local handler function name:                                  \n",
+              " retrieve_documents                                                   \n",
+              "╰──────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m────────────────────────────\u001b[0m\u001b[38;5;173m Obtained local function execution result \u001b[0m\u001b[38;5;173m─────────────────────────────\u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'status': 'not_found', 'message': \"No documents found for query: 'RAG workflow'\", 'documents': []}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭───────────────────────────── Obtained local function execution result ──────────────────────────────╮\n",
+              " {'status': 'not_found', 'message': \"No documents found for query: 'RAG workflow'\", 'documents': []} \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m──────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[{'action_id': 'de6e59b8-0345-47e5-9a7c-a7713ea5cc89', 'performed_action_type': \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mfound for query: \\'RAG workflow\\'\", \"documents\": []}'}]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaafgqsft6h2c2zcphmd3ljne454upzegolf3owdudlcaxa\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────── Chat request to remote agent: None ───────────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " user message:                                                                                                   \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " performed actions by client:                                                                                    \n",
+              " [{'action_id': 'de6e59b8-0345-47e5-9a7c-a7713ea5cc89', 'performed_action_type':                                 \n",
+              " 'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents  \n",
+              " found for query: \\'RAG workflow\\'\", \"documents\": []}'}]                                                         \n",
+              "                                                                                                                 \n",
+              " session id:                                                                                                     \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaafgqsft6h2c2zcphmd3ljne454upzegolf3owdudlcaxa           \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"action_id\": \"bdbb1d6c-7448-4f84-9a17-2e41ecf818b9\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"function_call\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"name\": \"retrieve_documents\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"arguments\": \"{\\\"query\\\": \\\"RAG architecture\\\"}\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────── Chat response from remote agent ──────────────────╮\n",
+              " (Local <-- Remote)                                                  \n",
+              "                                                                     \n",
+              " agent message:                                                      \n",
+              " null                                                                \n",
+              "                                                                     \n",
+              " required actions for client to take:                                \n",
+              " [                                                                   \n",
+              "     {                                                               \n",
+              "         \"action_id\": \"bdbb1d6c-7448-4f84-9a17-2e41ecf818b9\",        \n",
+              "         \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\", \n",
+              "         \"function_call\": {                                          \n",
+              "             \"name\": \"retrieve_documents\",                           \n",
+              "             \"arguments\": \"{\\\"query\\\": \\\"RAG architecture\\\"}\"        \n",
+              "         }                                                           \n",
+              "     }                                                               \n",
+              " ]                                                                   \n",
+              "                                                                     \n",
+              " guardrail result:                                                   \n",
+              " None                                                                \n",
+              "                                                                     \n",
+              "                                                                     \n",
+              "╰─────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m Function call requested by agent and mapped local handler function \u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool call arguments: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'query': 'RAG architecture'}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Mapped local handler function name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Function call requested by agent and mapped local handler function ─╮\n",
+              " Agent function tool name:                                            \n",
+              " retrieve_documents                                                   \n",
+              "                                                                      \n",
+              " Agent function tool call arguments:                                  \n",
+              " {'query': 'RAG architecture'}                                        \n",
+              "                                                                      \n",
+              " Mapped local handler function name:                                  \n",
+              " retrieve_documents                                                   \n",
+              "╰──────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m──────────────────────────────\u001b[0m\u001b[38;5;173m Obtained local function execution result \u001b[0m\u001b[38;5;173m───────────────────────────────\u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'status': 'not_found', 'message': \"No documents found for query: 'RAG architecture'\", 'documents': []}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─────────────────────────────── Obtained local function execution result ────────────────────────────────╮\n",
+              " {'status': 'not_found', 'message': \"No documents found for query: 'RAG architecture'\", 'documents': []} \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m──────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[{'action_id': 'bdbb1d6c-7448-4f84-9a17-2e41ecf818b9', 'performed_action_type': \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mfound for query: \\'RAG architecture\\'\", \"documents\": []}'}]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaafgqsft6h2c2zcphmd3ljne454upzegolf3owdudlcaxa\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────── Chat request to remote agent: None ───────────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " user message:                                                                                                   \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " performed actions by client:                                                                                    \n",
+              " [{'action_id': 'bdbb1d6c-7448-4f84-9a17-2e41ecf818b9', 'performed_action_type':                                 \n",
+              " 'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents  \n",
+              " found for query: \\'RAG architecture\\'\", \"documents\": []}'}]                                                     \n",
+              "                                                                                                                 \n",
+              " session id:                                                                                                     \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaafgqsft6h2c2zcphmd3ljne454upzegolf3owdudlcaxa           \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m───────────────────────────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m───────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m{\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"role\": \"AGENT\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"content\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"text\": \"RAG (Retrieval-Augmented Generation) is a technology that enables the creation of a \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mdistributed database with high-performance, low-overhead availability, and fast failover with zero data loss. \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mIt is built into Oracle Globally Distributed Database and utilizes Raft replication to create smaller \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mreplication units that are automatically distributed among shards. RAG works by creating a distributed database\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mthat can be deployed and configured, and it is part of a larger ecosystem that includes other technologies such\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mas Oracle Autonomous Database, GraalVM Native Image, Spring Boot, Micronaut, Helidon, and Quarkus. Overall, RAG\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mis a powerful technology that enables the creation of highly available and scalable distributed databases, with\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mautomatic replication and failover capabilities.\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"citations\": null,\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"paragraph_citations\": null\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m },\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"time_created\": \"2026-01-28T13:52:43.606000+00:00\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m}\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭──────────────────────────────────────── Chat response from remote agent ────────────────────────────────────────╮\n",
+              " (Local <-- Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " agent message:                                                                                                  \n",
+              " {                                                                                                               \n",
+              "     \"role\": \"AGENT\",                                                                                            \n",
+              "     \"content\": {                                                                                                \n",
+              "         \"text\": \"RAG (Retrieval-Augmented Generation) is a technology that enables the creation of a            \n",
+              " distributed database with high-performance, low-overhead availability, and fast failover with zero data loss.   \n",
+              " It is built into Oracle Globally Distributed Database and utilizes Raft replication to create smaller           \n",
+              " replication units that are automatically distributed among shards. RAG works by creating a distributed database \n",
+              " that can be deployed and configured, and it is part of a larger ecosystem that includes other technologies such \n",
+              " as Oracle Autonomous Database, GraalVM Native Image, Spring Boot, Micronaut, Helidon, and Quarkus. Overall, RAG \n",
+              " is a powerful technology that enables the creation of highly available and scalable distributed databases, with \n",
+              " automatic replication and failover capabilities.\",                                                              \n",
+              "         \"citations\": null,                                                                                      \n",
+              "         \"paragraph_citations\": null                                                                             \n",
+              "     },                                                                                                          \n",
+              "     \"time_created\": \"2026-01-28T13:52:43.606000+00:00\"                                                          \n",
+              " }                                                                                                               \n",
+              "                                                                                                                 \n",
+              " required actions for client to take:                                                                            \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " guardrail result:                                                                                               \n",
+              " None                                                                                                            \n",
+              "                                                                                                                 \n",
+              "                                                                                                                 \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "🤖 Agent Response:\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────────────\u001b[0m\u001b[34m Agent run response \u001b[0m\u001b[34m──────────────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m agent text message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mRAG (Retrieval-Augmented Generation) is a technology that enables the creation of a distributed database with \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mhigh-performance, low-overhead availability, and fast failover with zero data loss. It is built into Oracle \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mGlobally Distributed Database and utilizes Raft replication to create smaller replication units that are \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mautomatically distributed among shards. RAG works by creating a distributed database that can be deployed and \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mconfigured, and it is part of a larger ecosystem that includes other technologies such as Oracle Autonomous \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mDatabase, GraalVM Native Image, Spring Boot, Micronaut, Helidon, and Quarkus. Overall, RAG is a powerful \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mtechnology that enables the creation of highly available and scalable distributed databases, with automatic \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mreplication and failover capabilities.\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────────────── Agent run response ───────────────────────────────────────────────╮\n",
+              " agent text message:                                                                                             \n",
+              " RAG (Retrieval-Augmented Generation) is a technology that enables the creation of a distributed database with   \n",
+              " high-performance, low-overhead availability, and fast failover with zero data loss. It is built into Oracle     \n",
+              " Globally Distributed Database and utilizes Raft replication to create smaller replication units that are        \n",
+              " automatically distributed among shards. RAG works by creating a distributed database that can be deployed and   \n",
+              " configured, and it is part of a larger ecosystem that includes other technologies such as Oracle Autonomous     \n",
+              " Database, GraalVM Native Image, Spring Boot, Micronaut, Helidon, and Quarkus. Overall, RAG is a powerful        \n",
+              " technology that enables the creation of highly available and scalable distributed databases, with automatic     \n",
+              " replication and failover capabilities.                                                                          \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "📝 User: Tell me about OCI Database services\n", + "------------------------------------------------------------\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m─────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mTell me about OCI Database services\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭───────────────────────────────── Chat request to remote agent: None ──────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                    \n",
+              "                                                                                                       \n",
+              " user message:                                                                                         \n",
+              " Tell me about OCI Database services                                                                   \n",
+              "                                                                                                       \n",
+              " performed actions by client:                                                                          \n",
+              " []                                                                                                    \n",
+              "                                                                                                       \n",
+              " session id:                                                                                           \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq \n",
+              "╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"action_id\": \"1880224b-ce5d-4092-a688-60ebd2020cfd\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"function_call\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"name\": \"retrieve_documents\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"arguments\": \"{\\\"query\\\": \\\"OCI Database services\\\"}\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────── Chat response from remote agent ──────────────────╮\n",
+              " (Local <-- Remote)                                                  \n",
+              "                                                                     \n",
+              " agent message:                                                      \n",
+              " null                                                                \n",
+              "                                                                     \n",
+              " required actions for client to take:                                \n",
+              " [                                                                   \n",
+              "     {                                                               \n",
+              "         \"action_id\": \"1880224b-ce5d-4092-a688-60ebd2020cfd\",        \n",
+              "         \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\", \n",
+              "         \"function_call\": {                                          \n",
+              "             \"name\": \"retrieve_documents\",                           \n",
+              "             \"arguments\": \"{\\\"query\\\": \\\"OCI Database services\\\"}\"   \n",
+              "         }                                                           \n",
+              "     }                                                               \n",
+              " ]                                                                   \n",
+              "                                                                     \n",
+              " guardrail result:                                                   \n",
+              " None                                                                \n",
+              "                                                                     \n",
+              "                                                                     \n",
+              "╰─────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m Function call requested by agent and mapped local handler function \u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool call arguments: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'query': 'OCI Database services'}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Mapped local handler function name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Function call requested by agent and mapped local handler function ─╮\n",
+              " Agent function tool name:                                            \n",
+              " retrieve_documents                                                   \n",
+              "                                                                      \n",
+              " Agent function tool call arguments:                                  \n",
+              " {'query': 'OCI Database services'}                                   \n",
+              "                                                                      \n",
+              " Mapped local handler function name:                                  \n",
+              " retrieve_documents                                                   \n",
+              "╰──────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m─────────────────────────────────\u001b[0m\u001b[38;5;173m Obtained local function execution result \u001b[0m\u001b[38;5;173m─────────────────────────────────\u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'status': 'not_found', 'message': \"No documents found for query: 'OCI Database services'\", 'documents': []}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────── Obtained local function execution result ──────────────────────────────────╮\n",
+              " {'status': 'not_found', 'message': \"No documents found for query: 'OCI Database services'\", 'documents': []} \n",
+              "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m──────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[{'action_id': '1880224b-ce5d-4092-a688-60ebd2020cfd', 'performed_action_type': \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mfound for query: \\'OCI Database services\\'\", \"documents\": []}'}]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────── Chat request to remote agent: None ───────────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " user message:                                                                                                   \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " performed actions by client:                                                                                    \n",
+              " [{'action_id': '1880224b-ce5d-4092-a688-60ebd2020cfd', 'performed_action_type':                                 \n",
+              " 'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents  \n",
+              " found for query: \\'OCI Database services\\'\", \"documents\": []}'}]                                                \n",
+              "                                                                                                                 \n",
+              " session id:                                                                                                     \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq           \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m─────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"action_id\": \"35297f85-b678-45fc-9e3c-9afc53320c73\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"function_call\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"name\": \"search_oci_knowledge_base\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"arguments\": \"{\\\"topic\\\": \\\"database\\\", \\\"category\\\": \\\"all\\\"}\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────── Chat response from remote agent ──────────────────────╮\n",
+              " (Local <-- Remote)                                                          \n",
+              "                                                                             \n",
+              " agent message:                                                              \n",
+              " null                                                                        \n",
+              "                                                                             \n",
+              " required actions for client to take:                                        \n",
+              " [                                                                           \n",
+              "     {                                                                       \n",
+              "         \"action_id\": \"35297f85-b678-45fc-9e3c-9afc53320c73\",                \n",
+              "         \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",         \n",
+              "         \"function_call\": {                                                  \n",
+              "             \"name\": \"search_oci_knowledge_base\",                            \n",
+              "             \"arguments\": \"{\\\"topic\\\": \\\"database\\\", \\\"category\\\": \\\"all\\\"}\" \n",
+              "         }                                                                   \n",
+              "     }                                                                       \n",
+              " ]                                                                           \n",
+              "                                                                             \n",
+              " guardrail result:                                                           \n",
+              " None                                                                        \n",
+              "                                                                             \n",
+              "                                                                             \n",
+              "╰─────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m Function call requested by agent and mapped local handler function \u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36msearch_oci_knowledge_base\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool call arguments: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'topic': 'database', 'category': 'all'}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Mapped local handler function name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36msearch_oci_knowledge_base\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Function call requested by agent and mapped local handler function ─╮\n",
+              " Agent function tool name:                                            \n",
+              " search_oci_knowledge_base                                            \n",
+              "                                                                      \n",
+              " Agent function tool call arguments:                                  \n",
+              " {'topic': 'database', 'category': 'all'}                             \n",
+              "                                                                      \n",
+              " Mapped local handler function name:                                  \n",
+              " search_oci_knowledge_base                                            \n",
+              "╰──────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m──────────────────────────────────\u001b[0m\u001b[38;5;173m Obtained local function execution result \u001b[0m\u001b[38;5;173m───────────────────────────────────\u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'topic': 'database', 'category': 'all', 'results': [{'category': 'database', 'item': 'Autonomous Database'}, \u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'category': 'database', 'item': 'MySQL Database'}, {'category': 'database', 'item': 'PostgreSQL Database'}, \u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'category': 'database', 'item': 'NoSQL Database'}, {'category': 'database', 'item': 'Database Backup'}], \u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m'count': 5}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─────────────────────────────────── Obtained local function execution result ────────────────────────────────────╮\n",
+              " {'topic': 'database', 'category': 'all', 'results': [{'category': 'database', 'item': 'Autonomous Database'},   \n",
+              " {'category': 'database', 'item': 'MySQL Database'}, {'category': 'database', 'item': 'PostgreSQL Database'},    \n",
+              " {'category': 'database', 'item': 'NoSQL Database'}, {'category': 'database', 'item': 'Database Backup'}],       \n",
+              " 'count': 5}                                                                                                     \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m──────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[{'action_id': '35297f85-b678-45fc-9e3c-9afc53320c73', 'performed_action_type': \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"topic\": \"database\", \"category\": \"all\", \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m\"results\": [{\"category\": \"database\", \"item\": \"Autonomous Database\"}, {\"category\": \"database\", \"item\": \"MySQL \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mDatabase\"}, {\"category\": \"database\", \"item\": \"PostgreSQL Database\"}, {\"category\": \"database\", \"item\": \"NoSQL \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mDatabase\"}, {\"category\": \"database\", \"item\": \"Database Backup\"}], \"count\": 5}'}]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────── Chat request to remote agent: None ───────────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " user message:                                                                                                   \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " performed actions by client:                                                                                    \n",
+              " [{'action_id': '35297f85-b678-45fc-9e3c-9afc53320c73', 'performed_action_type':                                 \n",
+              " 'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"topic\": \"database\", \"category\": \"all\",          \n",
+              " \"results\": [{\"category\": \"database\", \"item\": \"Autonomous Database\"}, {\"category\": \"database\", \"item\": \"MySQL    \n",
+              " Database\"}, {\"category\": \"database\", \"item\": \"PostgreSQL Database\"}, {\"category\": \"database\", \"item\": \"NoSQL    \n",
+              " Database\"}, {\"category\": \"database\", \"item\": \"Database Backup\"}], \"count\": 5}'}]                                \n",
+              "                                                                                                                 \n",
+              " session id:                                                                                                     \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq           \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m─────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"action_id\": \"c4a41a7a-2028-4a52-ac35-f09845bc1c80\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"function_call\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"name\": \"retrieve_documents\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"arguments\": \"{\\\"query\\\": \\\"Oracle Cloud Infrastructure Autonomous Database\\\"}\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────── Chat response from remote agent ──────────────────────────────╮\n",
+              " (Local <-- Remote)                                                                          \n",
+              "                                                                                             \n",
+              " agent message:                                                                              \n",
+              " null                                                                                        \n",
+              "                                                                                             \n",
+              " required actions for client to take:                                                        \n",
+              " [                                                                                           \n",
+              "     {                                                                                       \n",
+              "         \"action_id\": \"c4a41a7a-2028-4a52-ac35-f09845bc1c80\",                                \n",
+              "         \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",                         \n",
+              "         \"function_call\": {                                                                  \n",
+              "             \"name\": \"retrieve_documents\",                                                   \n",
+              "             \"arguments\": \"{\\\"query\\\": \\\"Oracle Cloud Infrastructure Autonomous Database\\\"}\" \n",
+              "         }                                                                                   \n",
+              "     }                                                                                       \n",
+              " ]                                                                                           \n",
+              "                                                                                             \n",
+              " guardrail result:                                                                           \n",
+              " None                                                                                        \n",
+              "                                                                                             \n",
+              "                                                                                             \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m Function call requested by agent and mapped local handler function \u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool call arguments: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'query': 'Oracle Cloud Infrastructure Autonomous Database'}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Mapped local handler function name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Function call requested by agent and mapped local handler function ─╮\n",
+              " Agent function tool name:                                            \n",
+              " retrieve_documents                                                   \n",
+              "                                                                      \n",
+              " Agent function tool call arguments:                                  \n",
+              " {'query': 'Oracle Cloud Infrastructure Autonomous Database'}         \n",
+              "                                                                      \n",
+              " Mapped local handler function name:                                  \n",
+              " retrieve_documents                                                   \n",
+              "╰──────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m──────────────────────────────────\u001b[0m\u001b[38;5;173m Obtained local function execution result \u001b[0m\u001b[38;5;173m───────────────────────────────────\u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'status': 'not_found', 'message': \"No documents found for query: 'Oracle Cloud Infrastructure Autonomous \u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32mDatabase'\", 'documents': []}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─────────────────────────────────── Obtained local function execution result ────────────────────────────────────╮\n",
+              " {'status': 'not_found', 'message': \"No documents found for query: 'Oracle Cloud Infrastructure Autonomous       \n",
+              " Database'\", 'documents': []}                                                                                    \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m──────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[{'action_id': 'c4a41a7a-2028-4a52-ac35-f09845bc1c80', 'performed_action_type': \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mfound for query: \\'Oracle Cloud Infrastructure Autonomous Database\\'\", \"documents\": []}'}]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────── Chat request to remote agent: None ───────────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " user message:                                                                                                   \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " performed actions by client:                                                                                    \n",
+              " [{'action_id': 'c4a41a7a-2028-4a52-ac35-f09845bc1c80', 'performed_action_type':                                 \n",
+              " 'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents  \n",
+              " found for query: \\'Oracle Cloud Infrastructure Autonomous Database\\'\", \"documents\": []}'}]                      \n",
+              "                                                                                                                 \n",
+              " session id:                                                                                                     \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq           \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m─────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"action_id\": \"965f33f1-3641-4912-8634-4623902f4940\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"function_call\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"name\": \"retrieve_documents\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m \"arguments\": \"{\\\"query\\\": \\\"Autonomous Database\\\"}\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m }\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────── Chat response from remote agent ──────────────────╮\n",
+              " (Local <-- Remote)                                                  \n",
+              "                                                                     \n",
+              " agent message:                                                      \n",
+              " null                                                                \n",
+              "                                                                     \n",
+              " required actions for client to take:                                \n",
+              " [                                                                   \n",
+              "     {                                                               \n",
+              "         \"action_id\": \"965f33f1-3641-4912-8634-4623902f4940\",        \n",
+              "         \"required_action_type\": \"FUNCTION_CALLING_REQUIRED_ACTION\", \n",
+              "         \"function_call\": {                                          \n",
+              "             \"name\": \"retrieve_documents\",                           \n",
+              "             \"arguments\": \"{\\\"query\\\": \\\"Autonomous Database\\\"}\"     \n",
+              "         }                                                           \n",
+              "     }                                                               \n",
+              " ]                                                                   \n",
+              "                                                                     \n",
+              " guardrail result:                                                   \n",
+              " None                                                                \n",
+              "                                                                     \n",
+              "                                                                     \n",
+              "╰─────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m Function call requested by agent and mapped local handler function \u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Agent function tool call arguments: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'query': 'Autonomous Database'}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m Mapped local handler function name: \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;36mretrieve_documents\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰──────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭─ Function call requested by agent and mapped local handler function ─╮\n",
+              " Agent function tool name:                                            \n",
+              " retrieve_documents                                                   \n",
+              "                                                                      \n",
+              " Agent function tool call arguments:                                  \n",
+              " {'query': 'Autonomous Database'}                                     \n",
+              "                                                                      \n",
+              " Mapped local handler function name:                                  \n",
+              " retrieve_documents                                                   \n",
+              "╰──────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[38;5;173m╭─\u001b[0m\u001b[38;5;173m────────────────────────────────\u001b[0m\u001b[38;5;173m Obtained local function execution result \u001b[0m\u001b[38;5;173m────────────────────────────────\u001b[0m\u001b[38;5;173m─╮\u001b[0m\n", + "\u001b[38;5;173m│\u001b[0m \u001b[1;32m{'status': 'not_found', 'message': \"No documents found for query: 'Autonomous Database'\", 'documents': []}\u001b[0m \u001b[38;5;173m│\u001b[0m\n", + "\u001b[38;5;173m╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭───────────────────────────────── Obtained local function execution result ─────────────────────────────────╮\n",
+              " {'status': 'not_found', 'message': \"No documents found for query: 'Autonomous Database'\", 'documents': []} \n",
+              "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────\u001b[0m\u001b[34m Chat request to remote agent: None \u001b[0m\u001b[34m──────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local --> Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m user message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m performed actions by client: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m[{'action_id': '965f33f1-3641-4912-8634-4623902f4940', 'performed_action_type': \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35m'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mfound for query: \\'Autonomous Database\\'\", \"documents\": []}'}]\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m session id: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;36mocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────── Chat request to remote agent: None ───────────────────────────────────────╮\n",
+              " (Local --> Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " user message:                                                                                                   \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " performed actions by client:                                                                                    \n",
+              " [{'action_id': '965f33f1-3641-4912-8634-4623902f4940', 'performed_action_type':                                 \n",
+              " 'FUNCTION_CALLING_PERFORMED_ACTION', 'function_call_output': '{\"status\": \"not_found\", \"message\": \"No documents  \n",
+              " found for query: \\'Autonomous Database\\'\", \"documents\": []}'}]                                                  \n",
+              "                                                                                                                 \n",
+              " session id:                                                                                                     \n",
+              " ocid1.genaiagentsession.oc1.us-chicago-1.amaaaaaa7mjirbaa2r7svbkdk4hvepwiairkcsgsrhmdom6xbvznvvch4dqq           \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m───────────────────────────────────────\u001b[0m\u001b[34m Chat response from remote agent \u001b[0m\u001b[34m───────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m (Local <-- Remote) \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m agent message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m{\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"role\": \"AGENT\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"content\": {\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"text\": \"Oracle Cloud Infrastructure (OCI) offers a range of database services, including Autonomous \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mDatabase, MySQL Database, PostgreSQL Database, NoSQL Database, and Database Backup. While I couldn't find \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mspecific documents about these services, you can refer to the Oracle Cloud Infrastructure documentation and \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mknowledge base for more general information. Autonomous Database is a fully managed database service that \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mautomates many administrative tasks, providing a high level of performance, security, and availability. If you \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mhave any more specific questions or need further assistance, feel free to ask.\",\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"citations\": null,\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"paragraph_citations\": null\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m },\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m \"time_created\": \"2026-01-28T13:53:15.138000+00:00\"\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32m}\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m required actions for client to take: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;35mnull\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m guardrail result: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭──────────────────────────────────────── Chat response from remote agent ────────────────────────────────────────╮\n",
+              " (Local <-- Remote)                                                                                              \n",
+              "                                                                                                                 \n",
+              " agent message:                                                                                                  \n",
+              " {                                                                                                               \n",
+              "     \"role\": \"AGENT\",                                                                                            \n",
+              "     \"content\": {                                                                                                \n",
+              "         \"text\": \"Oracle Cloud Infrastructure (OCI) offers a range of database services, including Autonomous    \n",
+              " Database, MySQL Database, PostgreSQL Database, NoSQL Database, and Database Backup. While I couldn't find       \n",
+              " specific documents about these services, you can refer to the Oracle Cloud Infrastructure documentation and     \n",
+              " knowledge base for more general information. Autonomous Database is a fully managed database service that       \n",
+              " automates many administrative tasks, providing a high level of performance, security, and availability. If you  \n",
+              " have any more specific questions or need further assistance, feel free to ask.\",                                \n",
+              "         \"citations\": null,                                                                                      \n",
+              "         \"paragraph_citations\": null                                                                             \n",
+              "     },                                                                                                          \n",
+              "     \"time_created\": \"2026-01-28T13:53:15.138000+00:00\"                                                          \n",
+              " }                                                                                                               \n",
+              "                                                                                                                 \n",
+              " required actions for client to take:                                                                            \n",
+              " null                                                                                                            \n",
+              "                                                                                                                 \n",
+              " guardrail result:                                                                                               \n",
+              " None                                                                                                            \n",
+              "                                                                                                                 \n",
+              "                                                                                                                 \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "🤖 Agent Response:\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[34m╭─\u001b[0m\u001b[34m─────────────────────────────────────────────\u001b[0m\u001b[34m Agent run response \u001b[0m\u001b[34m──────────────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n", + "\u001b[34m│\u001b[0m agent text message: \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mOracle Cloud Infrastructure (OCI) offers a range of database services, including Autonomous Database, MySQL \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mDatabase, PostgreSQL Database, NoSQL Database, and Database Backup. While I couldn't find specific documents \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mabout these services, you can refer to the Oracle Cloud Infrastructure documentation and knowledge base for \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mmore general information. Autonomous Database is a fully managed database service that automates many \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32madministrative tasks, providing a high level of performance, security, and availability. If you have any more \u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1;32mspecific questions or need further assistance, feel free to ask.\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ], + "text/html": [ + "
╭────────────────────────────────────────────── Agent run response ───────────────────────────────────────────────╮\n",
+              " agent text message:                                                                                             \n",
+              " Oracle Cloud Infrastructure (OCI) offers a range of database services, including Autonomous Database, MySQL     \n",
+              " Database, PostgreSQL Database, NoSQL Database, and Database Backup. While I couldn't find specific documents    \n",
+              " about these services, you can refer to the Oracle Cloud Infrastructure documentation and knowledge base for     \n",
+              " more general information. Autonomous Database is a fully managed database service that automates many           \n",
+              " administrative tasks, providing a high level of performance, security, and availability. If you have any more   \n",
+              " specific questions or need further assistance, feel free to ask.                                                \n",
+              "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "4. Conversation History Summary\n", + "----------------------------------------------------------------------\n", + "Total exchanges: 3\n", + "👤 USER: What is Database Sharding?...\n", + "🤖 ASSISTANT: raw_responses=[RawResponse(raw_data={'message': None, 'trace...\n", + "👤 USER: How does RAG work?...\n", + "🤖 ASSISTANT: raw_responses=[RawResponse(raw_data={'message': None, 'trace...\n", + "👤 USER: Tell me about OCI Database services...\n", + "🤖 ASSISTANT: raw_responses=[RawResponse(raw_data={'message': None, 'trace...\n" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/notebooks/rag-aiagent-chatbot/create-rag-aiagent-chatbot.md b/notebooks/rag-aiagent-chatbot/create-rag-aiagent-chatbot.md new file mode 100644 index 00000000..4cfbd1d1 --- /dev/null +++ b/notebooks/rag-aiagent-chatbot/create-rag-aiagent-chatbot.md @@ -0,0 +1,232 @@ +# Building a RAG Application with AI Agent Development Kit and Google Colab Notebook + +## Introduction + +Artificial intelligence is advancing rapidly, with AI agents emerging as powerful autonomous systems. These systems perceive their environment, execute actions, and learn from experiences, making them valuable tools for diverse applications. This guide explores AI agents, agentic AI, and retrieval-augmented generation (RAG) techniques, providing practical instructions for building a RAG application using Oracle Cloud Infrastructure (OCI). + +This guide covers the essential steps for creating an OCI Object Storage bucket, configuring a generative AI agent, establishing a RAG-based knowledge base, and deploying a Python-based application. Upon completion, you will understand how AI agents function and how to leverage RAG to build intelligent conversational interfaces that deliver accurate and contextually relevant responses. + +This also covers using [RAG Agent Development Kit](https://docs.oracle.com/en-us/iaas/Content/generative-ai-agents/adk/api-reference/introduction.htm) with Google Colab Notebook + +## AI Agents + +An AI agent is a software entity capable of: + +- Perceiving its environment and executing actions +- Learning from experiences and outcomes + +### Key Characteristics + +- Setting and pursuing goals +- Gathering and analyzing information +- Applying logic to plan execution steps +- Learning through trial and error (reinforcement learning) +- Utilizing tools and data sources to accomplish objectives + +### Capabilities + +AI agents demonstrate the following capabilities: + +- Understanding intent behind user queries +- Drawing on historical data and learned experiences +- Dynamically generating solutions to novel problems +- Replicating human-like reasoning patterns + +### Technologies + +AI agents leverage various technologies: + +- Large Language Models (LLMs) +- Natural Language Processing (NLP) +- Cloud services and APIs +- Machine learning-based systems + +## Agentic AI + +Agentic AI refers to AI systems capable of: + +- Making autonomous decisions based on past performance and current conditions +- Operating with minimal human oversight +- Adapting and adjusting strategies to achieve objectives + +### Key Characteristics + +- **Autonomy**: Makes decisions independently without human intervention +- **Flexibility**: Adapts by adding new steps, requesting assistance, or adjusting approaches as needed +- **Goal-Oriented**: Maintains focus on specific objectives throughout execution + +### Behavioral Model + +Agentic AI operates similarly to a manager who: + +- Deploys resources and expertise strategically +- Collaborates with team members and incorporates feedback +- Optimizes workflows and requests additional information when necessary + +Unlike traditional AI, agentic AI exhibits greater autonomy and adaptability, enabling it to handle complex tasks and make informed decisions with minimal human intervention. + +## Retrieval-Augmented Generation (RAG) +Retrieval-Augmented Generation (RAG) is a technique that enhances Large Language Model (LLM) outputs by integrating targeted, current, and context-specific information without modifying the underlying model. This approach enables generative AI systems to: + +- Provide more accurate and contextually relevant responses +- Access current data and real-time information +- Leverage organization-specific knowledge and domain expertise + +### Data Sources + +RAG systems ingest information from multiple sources: + +- Databases +- Data warehouses +- Documents and structured content +- News feeds and real-time data streams + +### Applications + +RAG enhances the value of AI systems for: + +- Conversational chatbots +- Question-answering systems +- Information retrieval and search + +### Example Use Case + +A sports organization can deploy RAG to deliver current information about games, player statistics, and team details, supplemented by general sports knowledge and historical context. +## Step 1: Create OCI Object Storage Bucket + +Log in to [cloud.oracle.com](https://cloud.oracle.com) and navigate to **Storage** > **Buckets** from the left navigation menu. + +![OCI Storage](images/01.png) + +Create a new bucket by providing a descriptive name. This bucket will store PDF documents and knowledge items that constitute your knowledge base. + +![OCI Storage](images/02.png) + +## Step 2: Create a Generative AI Agent + +From the left navigation, select **Analytics & AI** > **AI Services** > **Generative AI Agent**. + +![OCI Storage](images/03.png) + +If the Generative AI Agent option is not visible in the navigation menu, change your region to Chicago. If this does not resolve the issue, you may not have subscribed to this service. Contact your administrator to identify the subscribed region for your account. + +![OCI Storage](images/04.png) + +**Important**: The Object Storage bucket and RAG AI Agent must reside in the same region. On the **Overview** tab, click the **Create Agent** button. + +![RAG Agent](images/05.png) + +Provide the following basic information: + +- **Agent Name**: A descriptive name for your agent +- **Welcome Message**: A greeting message displayed to users +- **Routing Information**: Select "Always Invoke RAG tool first" + +![RAG Agent](images/06.png) + +Click **Next** to configure tools. In the tools configuration section: + +1. Select **RAG** as the tool type +2. Provide a descriptive name for the RAG tool +3. Add knowledge bases (created in the following steps) + +![RAG Agent](images/07.png) + +**Note**: SQL tools are also available for connecting to Oracle Database and generating SQL queries based on natural language input. SQL-based database interaction is not covered in this guide. + +![RAG Agent](images/08.png) + +## Step 3: Create a Knowledge Base + +Create a data source by selecting the OCI Object Storage bucket created in Step 1. + +![KB](images/09.png) + +Under the **Knowledge Bases** section in the tools menu, click **Create Knowledge Base**. + +![KB](images/10.png) + +Configure the knowledge base settings: + +1. Leave **Guardrails** options in their default disabled state +2. Click **Next** to proceed +3. Review and accept the license agreement +4. Click **Submit** to create the agent + +This action creates your Agent Endpoint and Agent instance. **Important**: Record the **Agent Endpoint OCID** as you will need this identifier for future integrations and API calls. + +## Step 4: Test Initial Agent Conversation + +Click the **Launch Chat** button to open the agent chat interface. The welcome message you configured will be displayed. + +![Chat](images/13.png) + +### Testing Query + +Test the agent by asking a question: + +``` +Who is Database Sharding? +``` + +Without trained knowledge, the agent responds: + +``` +Unsure about the answer based on the references. +``` + +This is expected behavior, as the knowledge base has not yet been populated with relevant data. + +## Step 5: Train the Agent with Knowledge Base Data + +To improve agent responses, populate the knowledge base with relevant documents. + +### Prepare Knowledge Documents + +Gather knowledge artifacts from appropriate sources. For example, retrieve information from [Oracle Database Sharding Guide](https://docs.oracle.com/cd/F39414_01/shard/oracle-globally-distributed-database-guide.pdf) and convert it to PDF format. + +### Upload to Object Storage + +Upload the PDF documents to the OCI Object Storage bucket created in Step 1. + +![Chat](images/15.png) + +### Initiate Data Ingestion + +Create a Data Ingestion Job: + +1. Navigate to **Knowledge Bases** > **Storage** > [Your Bucket Name] +2. Click **Create Ingestion Job** +3. Monitor the job status + +The ingestion job status will transition from **Accepted** to **Succeeded** within a few minutes. Upon completion, the knowledge base will be updated with the new documents. + +## Step 6: Test Agent with Trained Knowledge Base + +Now that the knowledge base has been populated with relevant documents, test the agent again with the same query: + +``` +Who is Database Sharding? +``` + +The agent now provides a comprehensive, informative response based on the ingested knowledge documents: + +![Chat](images/dbsharding.png) + +This demonstrates the effectiveness of RAG in enhancing agent responses with domain-specific knowledge. + +## Step 7: Run RAG AI Agent Chatbot with Google Colab + +Extend agent functionality by creating a Google Colab notebook that connects to your RAG AI Agent Endpoint for programmatic chat interactions. + +Access the reference implementation: + +[Download Google Colab Notebook](RAGChatbotwithAgentDevelopmentKit.ipynb) + +This notebook demonstrates how to interact with your RAG agent using [RAG Agent Development Kit](https://docs.oracle.com/en-us/iaas/Content/generative-ai-agents/adk/api-reference/introduction.htm), enabling integration with additional workflows and applications. + +## Author + +Madhusudhan Rao, Principal Product Manager, Oracle Database + +Jan 28th, 2026 \ No newline at end of file diff --git a/notebooks/rag-aiagent-chatbot/images/01.png b/notebooks/rag-aiagent-chatbot/images/01.png new file mode 100644 index 00000000..b692904d Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/01.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/02.png b/notebooks/rag-aiagent-chatbot/images/02.png new file mode 100644 index 00000000..40a634b1 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/02.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/03.png b/notebooks/rag-aiagent-chatbot/images/03.png new file mode 100644 index 00000000..9a3b5ba0 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/03.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/04.png b/notebooks/rag-aiagent-chatbot/images/04.png new file mode 100644 index 00000000..3fb15c2d Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/04.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/05.png b/notebooks/rag-aiagent-chatbot/images/05.png new file mode 100644 index 00000000..1daa4569 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/05.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/06.png b/notebooks/rag-aiagent-chatbot/images/06.png new file mode 100644 index 00000000..57fe72f8 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/06.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/07.png b/notebooks/rag-aiagent-chatbot/images/07.png new file mode 100644 index 00000000..e6deb15d Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/07.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/08.png b/notebooks/rag-aiagent-chatbot/images/08.png new file mode 100644 index 00000000..91fd9f64 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/08.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/09.png b/notebooks/rag-aiagent-chatbot/images/09.png new file mode 100644 index 00000000..284acbfa Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/09.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/10.png b/notebooks/rag-aiagent-chatbot/images/10.png new file mode 100644 index 00000000..45dac6c8 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/10.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/11.png b/notebooks/rag-aiagent-chatbot/images/11.png new file mode 100644 index 00000000..77827356 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/11.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/12.png b/notebooks/rag-aiagent-chatbot/images/12.png new file mode 100644 index 00000000..8fb25be2 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/12.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/13.png b/notebooks/rag-aiagent-chatbot/images/13.png new file mode 100644 index 00000000..44b70592 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/13.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/14.png b/notebooks/rag-aiagent-chatbot/images/14.png new file mode 100644 index 00000000..95a947d2 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/14.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/15.png b/notebooks/rag-aiagent-chatbot/images/15.png new file mode 100644 index 00000000..19f34c96 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/15.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/16.png b/notebooks/rag-aiagent-chatbot/images/16.png new file mode 100644 index 00000000..97784cc5 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/16.png differ diff --git a/notebooks/rag-aiagent-chatbot/images/dbsharding.png b/notebooks/rag-aiagent-chatbot/images/dbsharding.png new file mode 100644 index 00000000..4a1ac9a4 Binary files /dev/null and b/notebooks/rag-aiagent-chatbot/images/dbsharding.png differ diff --git a/notebooks/similarity-search-19c/README-similarity-search.md b/notebooks/similarity-search-19c/README-similarity-search.md new file mode 100644 index 00000000..e48a4216 --- /dev/null +++ b/notebooks/similarity-search-19c/README-similarity-search.md @@ -0,0 +1,190 @@ +# How Can I Create Semantic Similarity Search in Oracle Database 19c? + +## Introduction + +In this lab, you will learn how to **create a similarity search application using LangChain, Streamlit, and Sentence Transformers Hugging Face Model with Oracle Autonomous Database 19c**. + +**Similarity search** is a technique that finds items in a dataset that are most similar to a query, rather than looking for exact matches. It works by representing items as numerical vectors (called embeddings) and then using mathematical formulas like cosine similarity or Euclidean distance to find the vectors closest to the query vector. This allows systems to find related items based on shared features, making it useful for applications like image and text search, recommendation systems, and AI-driven analysis. + +**Semantic search** is a technique that understands the contextual meaning and intent behind a user's query, going beyond simple keyword matching to find relevant results. It works by converting text into numerical representations called vectors, which are stored in a vector database. The system then compares the vector of a new query to existing vectors to find the most similar ones, effectively connecting ideas even when the exact words are different. + +How it works + +* **Vectorization**: Data, such as text, images, or audio, is converted into numerical vectors (embeddings). These vectors capture the essence or features of the original item. +* **Similarity calculation**: A similarity measure is used to calculate the "distance" or "angle" between the query vector and the vectors of the items in the dataset. +* **Ranking**: Items are ranked based on their similarity to the query, with the most similar items appearing first. +Retrieval: The system retrieves the items with the highest similarity scores + +[SentenceTransformers](https://huggingface.co/sentence-transformers) is a Python framework for state-of-the-art sentence, text and image embeddings. + +[all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) is a sentence-transformers model: It maps sentences & paragraphs to a 384 dimensional dense vector space and can be used for tasks like clustering or semantic search. + +Estimated Time: 2 to 5 minutes. + +### Objectives + +In this short sprint, you will: + +* Create a similarity search application using LangChain, Streamlit and Sentence Transformers Hugging Face Mode. + + + +### Prerequisites + +This lab assumes you have: + +* Oracle cloud account and privileges to create & manage Oracle Autonomous AI Database +* Oracle Autonomous Database 19c has been created and is running. +* Basic knowledge of Python programming language. +* Basic knowledge of SQL and Oracle Database concepts. + +**Download Source Code** - similaritysearch.py and Create Table script - create-table.sql from [GitHub Repository](https://github.com/madhusudhanrao-ppm/dbdevrel/tree/main/source-codes/similaritysearch) + +## Task 1: Create Table and Insert Sample Records + +1. Create Table in Oracle Database 19c (Alternatively You can also use 23ai or 26ai) and insert few sample records + + ``` + + CREATE TABLE "MYNOTES" + ( + "ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 99999999 + INCREMENT BY 1 START WITH 4 CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE, + "NOTES" VARCHAR2(4000 CHAR), + CONSTRAINT "MYNOTES_ID_PK" PRIMARY KEY ("ID") + USING INDEX ENABLE + ) ; + + -- Insert records + INSERT INTO MYNOTES (notes) VALUES ('Travel to Kashmir was an unforgettable experience with stunning landscapes.'); + INSERT INTO MYNOTES (notes) VALUES ('Hotel Booking in Delhi was confirmed for the weekend.'); + INSERT INTO MYNOTES (notes) VALUES ('Arrived in Paris, the Eiffel Tower was breathtaking at sunset.'); + INSERT INTO MYNOTES (notes) VALUES ('Loved the croissants at a tiny bakery near the Seine.'); + INSERT INTO MYNOTES (notes) VALUES ('Tokyo streets are incredibly clean and efficient.'); + INSERT INTO MYNOTES (notes) VALUES ('Central Park was a perfect escape from the city buzz.'); + INSERT INTO MYNOTES (notes) VALUES ('Bangkok night markets are a must‑try for street food.'); + INSERT INTO MYNOTES (notes) VALUES ('Sydney Opera House illuminated at night is unforgettable.'); + INSERT INTO MYNOTES (notes) VALUES ('Rome’s ancient ruins are best explored with a guide.'); + INSERT INTO MYNOTES (notes) VALUES ('Tapas in Barcelona were small but packed with flavor.'); + INSERT INTO MYNOTES (notes) VALUES ('Dubai’s desert safari was an exhilarating experience.'); + INSERT INTO MYNOTES (notes) VALUES ('Cape Town’s Table Mountain offered stunning panoramic views.'); + INSERT INTO MYNOTES (notes) VALUES ('Patient tolerated surgery well; margins clear.'); + INSERT INTO MYNOTES (notes) VALUES ('Started on pembrolizumab - mild fatigue reported.'); + INSERT INTO MYNOTES (notes) VALUES ('Completed 6 cycles of FOLFOX; CEA levels decreasing.'); + INSERT INTO MYNOTES (notes) VALUES ('Androgen deprivation therapy initiated; PSA dropped to 0.8 ng/mL.'); + INSERT INTO MYNOTES (notes) VALUES ('Bilateral lung metastases stable after 8 cycles of nivolumab.'); + INSERT INTO MYNOTES (notes) VALUES ('CR achieved after induction; proceeding to consolidation.'); + INSERT INTO MYNOTES (notes) VALUES ('Started on gemcitabine + nab‑paclitaxel; mild neutropenia noted.'); + INSERT INTO MYNOTES (notes) VALUES ('Debulking surgery successful; plan for platinum‑based chemo.'); + INSERT INTO MYNOTES (notes) VALUES ('PET scan shows complete metabolic response after 4 cycles of ABVD.'); + INSERT INTO MYNOTES (notes) VALUES ('Total thyroidectomy performed; awaiting radioactive iodine.'); + INSERT INTO MYNOTES (notes) VALUES ('Breast cancer screening scheduled for next month.'); + INSERT INTO MYNOTES (notes) VALUES ('Lung cancer screening scheduled for next month.'); + + ``` + +## Task 2: Download Wallet and Connection Details + +1. Check the Database version 19c is shown in the Database details page + + ![DB Conn](images/db-version.png ) + + From the top right navigation menu click on **Database Connection** button to download the wallet and connection details. + + ![DB Conn](images/db-conn.png ) + +2. Download wallet and Copy connection details. + + ![Wallet](images/copy-connection.png ) + + Provide wallet password and save the wallet. + +3. Copy and save TNS Name, which would be of the following format, where database name and region will change. we will need this details for database connection. + + ``` + + devdbhs556l_high = (description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1522)(host=adb.us-phoenix-1.oraclecloud.com))(connect_data=(service_name=wkrfstesta1jcu_devdbhs556l_high.adb.oraclecloud.com))(security=(ssl_server_dn_match=yes))) + + ``` + +## Task 3: Upload the Database Wallet files + +1. Upload the Database Wallet files to Google Drive and Authorize Drive with Google Colab + + +## Task 2: Download Wallet and Connection Details + +Download and securely store your Oracle Autonomous Database wallet, which contains the credentials needed to connect from Google Colab. + +1. In the OCI Console, navigate to your Autonomous Database instance. Note that you can use Oracle Autonomous AI Database 26ai for this lab. + + ![DB Conn](images/db-version.png) + + From the top-right navigation menu, click the **Database Connection** button to access wallet and connection options. + + ![DB Conn](images/db-conn.png) + +2. Download the wallet in ZIP format. You will be prompted to provide a wallet password for encryption. + + ![Wallet](images/copy-connection.png) + + Save the wallet file securely. You will need both the wallet files and the wallet password to connect from Google Colab. + +3. Extract the wallet ZIP file on your local machine. Inside, you'll find a `tnsnames.ora` file that contains TNS (Transparent Network Substrate) entries. Copy and save the TNS Name you plan to use. The format looks like: + + ``` + + mydb_high = (description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1522)(host=adb.us-phoenix-1.oraclecloud.com))(connect_data=(service_name=mydb_high.adb.oraclecloud.com))(security=(ssl_server_dn_match=yes))) + + ``` + + Note down: + - Your TNS alias name (e.g., `mydb_high`) + - Your database username (e.g., `ADMIN`) + - Your database password + - Your wallet password + + You will need these details in the later steps to establish the database connection from Google Colab. + +## Task 3: Upload Database Wallet Files to Google Drive + +Mount Google Drive in Google Colab and upload your wallet files for secure access during Python execution. + +**Security Note:** Mounting Google Drive in Colab requires explicit authorization each time you connect to a new Colab runtime. This ensures you maintain full control over what code can access. [Learn more about Colab Drive access](https://research.google.com/colaboratory/faq.html#drive-mount-code-cell) + +1. On your local machine, create a folder (e.g., `Wallet_MyDatabase`) containing all the wallet files extracted from the ZIP download. + +2. Upload this wallet folder to your Google Drive: + + ![Wallet](images/drive1.png) + +3. Open [Google Colab](https://colab.research.google.com/) and create a new notebook. In the left sidebar, click the **Files** icon to access file management. + + ![Wallet](images/drive4.png) + +4. Click the **Mount Drive** button or use code to mount Google Drive: + + ``` + + from google.colab import drive + drive.mount('/content/drive') + + ``` + + ![Wallet](images/mount.png) + +5. A dialog will appear asking you to authorize Google Colab to access your Google Drive. Click the authorization link, sign in with your Google account, copy the verification code, and paste it into the Colab prompt. + + ![Wallet](images/conndrive.png) + +6. After authorization, you will see your Google Drive folder structure, including your wallet folder. + + ![Wallet](images/drive3.png) + +7. Right-click on your wallet folder and select **Copy path** to get the full path. It will look similar to: + + ``` + /content/drive/MyDrive/Wallet_MyDatabase + ``` + + Keep this path for use in the Google Colab Notebook. \ No newline at end of file diff --git a/notebooks/similarity-search-19c/images/01.png b/notebooks/similarity-search-19c/images/01.png new file mode 100644 index 00000000..a319aa15 Binary files /dev/null and b/notebooks/similarity-search-19c/images/01.png differ diff --git a/notebooks/similarity-search-19c/images/02.png b/notebooks/similarity-search-19c/images/02.png new file mode 100644 index 00000000..5c2a1d46 Binary files /dev/null and b/notebooks/similarity-search-19c/images/02.png differ diff --git a/notebooks/similarity-search-19c/images/03.png b/notebooks/similarity-search-19c/images/03.png new file mode 100644 index 00000000..69fb5e3b Binary files /dev/null and b/notebooks/similarity-search-19c/images/03.png differ diff --git a/notebooks/similarity-search-19c/images/04.png b/notebooks/similarity-search-19c/images/04.png new file mode 100644 index 00000000..9212ca4b Binary files /dev/null and b/notebooks/similarity-search-19c/images/04.png differ diff --git a/notebooks/similarity-search-19c/images/05.png b/notebooks/similarity-search-19c/images/05.png new file mode 100644 index 00000000..0380b2a3 Binary files /dev/null and b/notebooks/similarity-search-19c/images/05.png differ diff --git a/notebooks/similarity-search-19c/images/conndrive.png b/notebooks/similarity-search-19c/images/conndrive.png new file mode 100644 index 00000000..d25d77ee Binary files /dev/null and b/notebooks/similarity-search-19c/images/conndrive.png differ diff --git a/notebooks/similarity-search-19c/images/copy-connection.png b/notebooks/similarity-search-19c/images/copy-connection.png new file mode 100644 index 00000000..ad682fc1 Binary files /dev/null and b/notebooks/similarity-search-19c/images/copy-connection.png differ diff --git a/notebooks/similarity-search-19c/images/create-user.png b/notebooks/similarity-search-19c/images/create-user.png new file mode 100644 index 00000000..a0c7c334 Binary files /dev/null and b/notebooks/similarity-search-19c/images/create-user.png differ diff --git a/notebooks/similarity-search-19c/images/create-user2.png b/notebooks/similarity-search-19c/images/create-user2.png new file mode 100644 index 00000000..eb1798e8 Binary files /dev/null and b/notebooks/similarity-search-19c/images/create-user2.png differ diff --git a/notebooks/similarity-search-19c/images/db-conn.png b/notebooks/similarity-search-19c/images/db-conn.png new file mode 100644 index 00000000..6cd49da2 Binary files /dev/null and b/notebooks/similarity-search-19c/images/db-conn.png differ diff --git a/notebooks/similarity-search-19c/images/db-version.png b/notebooks/similarity-search-19c/images/db-version.png new file mode 100644 index 00000000..7e893065 Binary files /dev/null and b/notebooks/similarity-search-19c/images/db-version.png differ diff --git a/notebooks/similarity-search-19c/images/drive1.png b/notebooks/similarity-search-19c/images/drive1.png new file mode 100644 index 00000000..8e9c8059 Binary files /dev/null and b/notebooks/similarity-search-19c/images/drive1.png differ diff --git a/notebooks/similarity-search-19c/images/drive3.png b/notebooks/similarity-search-19c/images/drive3.png new file mode 100644 index 00000000..660a7ea2 Binary files /dev/null and b/notebooks/similarity-search-19c/images/drive3.png differ diff --git a/notebooks/similarity-search-19c/images/drive4.png b/notebooks/similarity-search-19c/images/drive4.png new file mode 100644 index 00000000..bd937f0f Binary files /dev/null and b/notebooks/similarity-search-19c/images/drive4.png differ diff --git a/notebooks/similarity-search-19c/images/mount.png b/notebooks/similarity-search-19c/images/mount.png new file mode 100644 index 00000000..c0d39e7c Binary files /dev/null and b/notebooks/similarity-search-19c/images/mount.png differ diff --git a/notebooks/similarity-search-19c/similaritysearch-colab.ipynb b/notebooks/similarity-search-19c/similaritysearch-colab.ipynb new file mode 100644 index 00000000..4ac35f71 --- /dev/null +++ b/notebooks/similarity-search-19c/similaritysearch-colab.ipynb @@ -0,0 +1,206 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8fa2e6ea", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4a087b0", + "metadata": { + "id": "d4a087b0", + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "!pip install -q sentence-transformers faiss-cpu oracledb numpy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39d75d5e", + "metadata": { + "id": "39d75d5e", + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "import json\n", + "from typing import List, Dict, Optional\n", + "import numpy as np\n", + "import oracledb\n", + "from sentence_transformers import SentenceTransformer\n", + "import faiss\n", + "from google.colab import drive\n", + "drive.mount('/content/drive')\n", + "\n", + "MODEL_NAME = \"sentence-transformers/all-MiniLM-L6-v2\"\n", + "\n", + "def fetch_from_oracle(\n", + " table: str = \"MYNOTES\",\n", + " column: str = \"NOTES\",\n", + " max_rows: int = 1000,\n", + " use_wallet: bool = True,\n", + " wallet_dir: Optional[str] = None,\n", + ") -> List[str]:\n", + " \"\"\"Fetch text rows from Oracle. Uses env vars ORACLE_USER, ORACLE_PASSWORD, ORACLE_DSN.\n", + " If use_wallet=True, provide wallet_dir and wallet_password via env vars.\n", + " Returns list of strings (may be empty).\n", + " \"\"\"\n", + " username = \"DEMOUSER\" # Update with your username\n", + " password = \"YourPassword\" # Update with your password\n", + " tns_name = \"indeducation_high\"\n", + " wall_config_dir = \"/content/drive/MyDrive/Wallet_IndEducation\"\n", + " wall_pwd = \"walletpassword\"\n", + " table = \"MYNOTES\"\n", + " col = \"NOTES\"\n", + "\n", + " if not (username and password and tns_name):\n", + " print(\"Oracle credentials (ORACLE_USER, ORACLE_PASSWORD, ORACLE_DSN) not set. Returning empty list.\")\n", + " return []\n", + "\n", + " conn = None\n", + " try:\n", + " conn = oracledb.connect(user=username,\n", + " password=password,\n", + " dsn=tns_name,\n", + " config_dir=wall_config_dir,\n", + " wallet_location=wall_config_dir,\n", + " wallet_password=wall_pwd)\n", + "\n", + " # Always use config_dir and wallet_location if wall_config_dir is set,\n", + " # as TNS aliases often require it to find tnsnames.ora.\n", + "\n", + " print(\"✓ Successfully connected to Oracle Database\")\n", + "\n", + " cur = conn.cursor()\n", + " sql = f\"SELECT {column} FROM {table} WHERE {column} IS NOT NULL AND ROWNUM <= :maxrows\"\n", + " cur.execute(sql, [max_rows])\n", + " rows = cur.fetchall()\n", + " texts = [r[0] for r in rows if r and r[0] is not None]\n", + " cur.close()\n", + " return texts\n", + " except Exception as e:\n", + " print(f\"Error fetching from Oracle: {e}\")\n", + " return []\n", + " finally:\n", + " if conn:\n", + " try:\n", + " conn.close()\n", + " except Exception:\n", + " pass\n", + "\n", + "def build_embeddings(texts: List[str], model_name: str = MODEL_NAME, batch_size: int = 64):\n", + " \"\"\"Return (model, numpy array of embeddings L2-normalized).\"\"\"\n", + " model = SentenceTransformer(model_name)\n", + " embs = model.encode(texts, batch_size=batch_size, convert_to_numpy=True, show_progress_bar=True)\n", + " # normalize\n", + " faiss.normalize_L2(embs)\n", + " return model, embs\n", + "\n", + "def build_faiss_index(embs: np.ndarray):\n", + " \"\"\"Create an inner-product index. Inputs should be L2-normalized vectors; inner product == cosine similarity.\"\"\"\n", + " dim = embs.shape[1]\n", + " index = faiss.IndexFlatIP(dim)\n", + " index.add(embs)\n", + " return index\n", + "\n", + "def search_index(query: str, model: SentenceTransformer, index: faiss.IndexFlatIP, texts: List[str], k: int = 5):\n", + " q_emb = model.encode([query], convert_to_numpy=True)\n", + " faiss.normalize_L2(q_emb)\n", + " D, I = index.search(q_emb, k)\n", + " # D contains inner products (cosine similarity between -1 and 1)\n", + " results = []\n", + " for score, idx in zip(D[0], I[0]):\n", + " if idx < 0:\n", + " continue\n", + " results.append({\"index\": int(idx), \"text\": texts[idx], \"score\": float(score)})\n", + " return results\n", + "\n", + "def save_index(path_prefix: str, embs: np.ndarray, texts: List[str]):\n", + " np.save(f\"{path_prefix}_emb.npy\", embs)\n", + " with open(f\"{path_prefix}_texts.json\", \"w\", encoding=\"utf-8\") as f:\n", + " json.dump(texts, f, ensure_ascii=False)\n", + "\n", + "def load_index(path_prefix: str):\n", + " embs = np.load(f\"{path_prefix}_emb.npy\")\n", + " with open(f\"{path_prefix}_texts.json\", \"r\", encoding=\"utf-8\") as f:\n", + " texts = json.load(f)\n", + " index = build_faiss_index(embs)\n", + " return index, embs, texts\n", + "\n", + "\n", + "# Configuration: switch to True to fetch from Oracle\n", + "use_oracle = True\n", + "\n", + "texts = []\n", + "if use_oracle:\n", + " print(\"Fetching from Oracle...\")\n", + " # Note: 'use_wallet=False' was passed here, but the DSN might still implicitly require a config_dir.\n", + " # The hardcoded values for Oracle connection parameters inside fetch_from_oracle also need review.\n", + " texts = fetch_from_oracle(table=\"MYNOTES\", column=\"NOTES\", max_rows=1000, use_wallet=False)\n", + " print(f\"Fetched {len(texts)} rows\")\n", + "else:\n", + " texts = [\n", + " \"I want to open an account.\",\n", + " \"I want a credit card.\",\n", + " \"I need to update my address.\",\n", + " \"I want to apply for a loan.\",\n", + " \"How do I check my balance?\",\n", + " \"I lost my debit card.\"\n", + " ]\n", + " print(f\"Using {len(texts)} sample texts\")\n", + "\n", + "# Build model + embeddings + index\n", + "model = None # Initialize model to None\n", + "embs = None # Initialize embs to None\n", + "index = None # Initialize index to None\n", + "\n", + "if texts: # Only build embeddings and index if texts list is not empty\n", + " model, embs = build_embeddings(texts)\n", + " index = build_faiss_index(embs)\n", + " print(\"Index ready. Use search_index(query, model, index, texts, k)\")\n", + "else:\n", + " print(\"No texts available to build embeddings and index.\")\n", + " print(\"Please check the Oracle connection details or provide sample texts.\")\n", + "\n", + "\n", + "query = input(\"Enter search query: \")\n", + "if query.strip():\n", + " results = search_index(query, model, index, texts, k=5)\n", + " for i, r in enumerate(results, 1):\n", + " print(f\"{i}. score={r['score']:.4f}\\n {r['text'][:300]}\\n\")\n", + "else:\n", + " print(\"No query provided.\")" + ] + } + ], + "metadata": { + "colab": { + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/spring-boot-app/images/added.png b/notebooks/spring-boot-app/images/added.png new file mode 100644 index 00000000..40cc58fe Binary files /dev/null and b/notebooks/spring-boot-app/images/added.png differ diff --git a/notebooks/spring-boot-app/images/addnew.png b/notebooks/spring-boot-app/images/addnew.png new file mode 100644 index 00000000..3136849a Binary files /dev/null and b/notebooks/spring-boot-app/images/addnew.png differ diff --git a/notebooks/spring-boot-app/images/architecture.png b/notebooks/spring-boot-app/images/architecture.png new file mode 100644 index 00000000..e2e2a4d8 Binary files /dev/null and b/notebooks/spring-boot-app/images/architecture.png differ diff --git a/notebooks/spring-boot-app/images/bootextension.png b/notebooks/spring-boot-app/images/bootextension.png new file mode 100644 index 00000000..c89d8d07 Binary files /dev/null and b/notebooks/spring-boot-app/images/bootextension.png differ diff --git a/notebooks/spring-boot-app/images/boottools.png b/notebooks/spring-boot-app/images/boottools.png new file mode 100644 index 00000000..26fab325 Binary files /dev/null and b/notebooks/spring-boot-app/images/boottools.png differ diff --git a/notebooks/spring-boot-app/images/create-conn.png b/notebooks/spring-boot-app/images/create-conn.png new file mode 100644 index 00000000..eb3da819 Binary files /dev/null and b/notebooks/spring-boot-app/images/create-conn.png differ diff --git a/notebooks/spring-boot-app/images/dbwallet.png b/notebooks/spring-boot-app/images/dbwallet.png new file mode 100644 index 00000000..0fee6210 Binary files /dev/null and b/notebooks/spring-boot-app/images/dbwallet.png differ diff --git a/notebooks/spring-boot-app/images/extensionpack.png b/notebooks/spring-boot-app/images/extensionpack.png new file mode 100644 index 00000000..2d8126cf Binary files /dev/null and b/notebooks/spring-boot-app/images/extensionpack.png differ diff --git a/notebooks/spring-boot-app/images/runapp.png b/notebooks/spring-boot-app/images/runapp.png new file mode 100644 index 00000000..18e5b41a Binary files /dev/null and b/notebooks/spring-boot-app/images/runapp.png differ diff --git a/notebooks/spring-boot-app/images/update1.png b/notebooks/spring-boot-app/images/update1.png new file mode 100644 index 00000000..f4e5a8cd Binary files /dev/null and b/notebooks/spring-boot-app/images/update1.png differ diff --git a/notebooks/spring-boot-app/images/update2.png b/notebooks/spring-boot-app/images/update2.png new file mode 100644 index 00000000..078ba436 Binary files /dev/null and b/notebooks/spring-boot-app/images/update2.png differ diff --git a/notebooks/spring-boot-app/images/viewall.png b/notebooks/spring-boot-app/images/viewall.png new file mode 100644 index 00000000..f90f1c8c Binary files /dev/null and b/notebooks/spring-boot-app/images/viewall.png differ diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/HELP.md b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/HELP.md new file mode 100644 index 00000000..f6b11ab2 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/HELP.md @@ -0,0 +1,28 @@ +# Getting Started + +### Reference Documentation +For further reference, please consider the following sections: + +* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) +* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/3.5.7/maven-plugin) +* [Create an OCI image](https://docs.spring.io/spring-boot/3.5.7/maven-plugin/build-image.html) +* [Spring Web](https://docs.spring.io/spring-boot/3.5.7/reference/web/servlet.html) +* [Thymeleaf](https://docs.spring.io/spring-boot/3.5.7/reference/web/servlet.html#web.servlet.spring-mvc.template-engines) +* [Spring Data JPA](https://docs.spring.io/spring-boot/3.5.7/reference/data/sql.html#data.sql.jpa-and-spring-data) + +### Guides +The following guides illustrate how to use some features concretely: + +* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) +* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) +* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) +* [Handling Form Submission](https://spring.io/guides/gs/handling-form-submission/) +* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/) + +### Maven Parent overrides + +Due to Maven's design, elements are inherited from the parent POM to the project POM. +While most of the inheritance is fine, it also inherits unwanted elements like `` and `` from the parent. +To prevent this, the project POM contains empty overrides for these elements. +If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides. + diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/mvnw b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/mvnw new file mode 100755 index 00000000..bd8896bf --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/mvnw.cmd b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/mvnw.cmd new file mode 100644 index 00000000..92450f93 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/pom.xml b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/pom.xml new file mode 100644 index 00000000..9d7b4b52 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.7 + + + com.example + springdemoapp + 0.0.1-SNAPSHOT + war + springdemoapp + Demo project for Spring Boot with Oracle AI Database + + + + + + + + + + + + + + + 21 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + com.oracle.database.jdbc + ojdbc11-production + 21.5.0.0 + pom + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + jakarta.persistence + jakarta.persistence-api + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/BankcustomersApplication.java b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/BankcustomersApplication.java new file mode 100644 index 00000000..3bf4b0a6 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/BankcustomersApplication.java @@ -0,0 +1,17 @@ +package com.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/* +http://localhost:8080/customers +*/ + +@SpringBootApplication +public class BankcustomersApplication { + + public static void main(String[] args) { + SpringApplication.run(BankcustomersApplication.class, args); + } + +} diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/ServletInitializer.java b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/ServletInitializer.java new file mode 100644 index 00000000..b3d03626 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/ServletInitializer.java @@ -0,0 +1,13 @@ +package com.example; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(BankcustomersApplication.class); + } + +} diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/controller/CustomerController.java b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/controller/CustomerController.java new file mode 100644 index 00000000..ad606d17 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/controller/CustomerController.java @@ -0,0 +1,72 @@ +package com.example.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +import com.example.model.entity.Customer; +import com.example.model.service.CustomerService; + + +@Controller +public class CustomerController { + + private CustomerService customerService; + + public CustomerController(CustomerService customerService) { + super(); + this.customerService = customerService; + } + // handler method to handle list customers and return mode and view + @GetMapping("/customers") + public String listCustomers(Model model) { + model.addAttribute("customers", customerService.getAllCustomers()); + return "customers"; + } + @GetMapping("/customers/new") + public String createCustomerForm(Model model) { + // create customer object to hold customer form data + Customer customer = new Customer(); + model.addAttribute("customer", customer); + return "create_customer"; + } + @PostMapping("/customers") + public String saveCustomer(@ModelAttribute("customer") Customer customer) { + customerService.saveCustomer(customer); + return "redirect:/customers"; + } + @GetMapping("/customers/edit/{id}") + public String editCustomerForm(@PathVariable Long id, Model model) { + model.addAttribute("customer", customerService.getCustomerById(id)); + return "edit_customer"; + } + @PostMapping("/customers/{id}") + public String updateCustomer(@PathVariable Long id, @ModelAttribute("customer") Customer customer, Model model) { + // get customer from database by id + Customer existingCustomer = customerService.getCustomerById(id); + existingCustomer.setId(id); + existingCustomer.setCustomerName(customer.getCustomerName()); + existingCustomer.setEmail(customer.getEmail()); + existingCustomer.setGender(customer.getGender()); + existingCustomer.setMaritalStatus(customer.getMaritalStatus()); + existingCustomer.setStreetAddress(customer.getStreetAddress()); + existingCustomer.setCity(customer.getCity()); + existingCustomer.setState(customer.getState()); + existingCustomer.setPhoneNumber(customer.getPhoneNumber()); + customerService.updateCustomer(existingCustomer); + return "redirect:/customers"; + } + @GetMapping("/customers/{id}") + public String deleteCustomer(@PathVariable Long id) { + customerService.deleteCustomerById(id); + return "redirect:/customers"; + } + + + + + +} diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/entity/Customer.java b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/entity/Customer.java new file mode 100644 index 00000000..3bedede0 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/entity/Customer.java @@ -0,0 +1,133 @@ +package com.example.model.entity; + +import jakarta.persistence.*; + +/* +Please refer resources/schema.sql for the table structure. +The entity represents the data model of the Bank Customers table. It maps to a database table & columns. +*/ + +@Entity +@Table(name = "BANK_CUSTOMERS") + +public class Customer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "CUSTOMER_NAME") + private String customerName; + + @Column(name = "GENDER") + private String gender; + + @Column(name = "MARITAL_STATUS") + private String maritalStatus; + + @Column(name = "STREET_ADDRESS") + private String streetAddress; + + @Column(name = "CITY") + private String city; + + @Column(name = "STATE") + private String state; + + @Column(name = "PHONE_NUMBER") + private String phoneNumber; + + @Column(name = "EMAIL") + private String email; + + public Customer() { + } + + public Customer(String customerName, String gender, String maritalStatus, String streetAddress, String city, + String state, String phoneNumber, String email) { + this.customerName = customerName; + this.gender = gender; + this.maritalStatus = maritalStatus; + this.streetAddress = streetAddress; + this.city = city; + this.state = state; + this.phoneNumber = phoneNumber; + this.email = email; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public String getMaritalStatus() { + return maritalStatus; + } + + public void setMaritalStatus(String maritalStatus) { + this.maritalStatus = maritalStatus; + } + + public String getStreetAddress() { + return streetAddress; + } + + public void setStreetAddress(String streetAddress) { + this.streetAddress = streetAddress; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + + +} + diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/repository/CustomerRepository.java b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/repository/CustomerRepository.java new file mode 100644 index 00000000..f2df903e --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/repository/CustomerRepository.java @@ -0,0 +1,21 @@ +package com.example.model.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.model.entity.Customer; + +/* +The repository is responsible for providing data access operations on the entity, +such as saving, updating, retrieving, and deleting data. +It abstracts the database operations and provides a simplified interface for interacting with the data layer + +JpaRepository is a core interface within Spring Data Java Persistence API, designed to simplify data access +and persistence operations in Java applications using the Java Persistence API (JPA). +It acts as a powerful abstraction layer, significantly reducing the amount of boilerplate code +required for common database interactions. +*/ + + +public interface CustomerRepository extends JpaRepository { + +} diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/service/CustomerService.java b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/service/CustomerService.java new file mode 100644 index 00000000..e4d35e06 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/service/CustomerService.java @@ -0,0 +1,18 @@ +package com.example.model.service; + +import java.util.List; + +import com.example.model.entity.Customer; + +public interface CustomerService { + + List getAllCustomers(); + + Customer saveCustomer(Customer customer); + + Customer getCustomerById(Long id); + + Customer updateCustomer(Customer customer); + + void deleteCustomerById(Long id); +} diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/service/CustomerServiceImpl.java b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/service/CustomerServiceImpl.java new file mode 100644 index 00000000..0c1d94b3 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/java/com/example/model/service/CustomerServiceImpl.java @@ -0,0 +1,50 @@ +package com.example.model.service; + +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.example.model.entity.Customer; +import com.example.model.repository.CustomerRepository; + +/* +The service implementation class implements the Bank Customer interface (CustomerRepository) +and provides the actual implementation of the business logic +*/ + +@Service +public class CustomerServiceImpl implements CustomerService { + + private CustomerRepository customerRepository; + + public CustomerServiceImpl(CustomerRepository customerRepository) { + super(); + this.customerRepository = customerRepository; + } + + @Override + public List getAllCustomers() { + return customerRepository.findAll(); + } + + @Override + public Customer saveCustomer(Customer customer) { + return customerRepository.save(customer); + } + @Override + public Customer getCustomerById(Long id) { + return customerRepository.findById(id).get(); + } + + @Override + public Customer updateCustomer(Customer customer) { + return customerRepository.save(customer); + } + + @Override + public void deleteCustomerById(Long id) { + customerRepository.deleteById(id); + } + +} + \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/application.properties b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/application.properties new file mode 100644 index 00000000..e23420d5 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/application.properties @@ -0,0 +1,20 @@ +spring.application.name=bankcustomersapp +spring.datasource.driver-class-name=oracle.jdbc.OracleDriver +spring.datasource.url=jdbc:oracle:thin:@indeducation_high?TNS_ADMIN=/Users/Wallet_folder/ +spring.datasource.username= +spring.datasource.password= +spring.jpa.database-platform=org.hibernate.dialect.OracleDialect + +##Properties of UCP +oracle.jdbc.fanEnabled=false +spring.datasource.type=oracle.ucp.jdbc.PoolDataSource +spring.datasource.oracleucp.connection-factory-class-name=oracle.jdbc.pool.OracleDataSource +spring.datasource.oracleucp.sql-for-validate-connection=select * from dual +spring.datasource.oracleucp.connection-pool-name=connectionPoolName1 +spring.datasource.oracleucp.initial-pool-size=15 +spring.datasource.oracleucp.min-pool-size=10 +spring.datasource.oracleucp.max-pool-size=30 +##Logging properties for UCP +logging.level.root=trace +logging.file.name=logs.log +logging.level.oracle.ucp=trace \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/schema.sql b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/schema.sql new file mode 100644 index 00000000..05bc2c4a --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/schema.sql @@ -0,0 +1,17 @@ +CREATE TABLE "BANK_CUSTOMERS" +( + "ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 + MAXVALUE 99999 + INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE + NOKEEP NOSCALE NOT NULL ENABLE, + "CUSTOMER_NAME" VARCHAR2(100), + "GENDER" VARCHAR2(10), + "MARITAL_STATUS" VARCHAR2(50), + "STREET_ADDRESS" VARCHAR2(255), + "CITY" VARCHAR2(50), + "STATE" VARCHAR2(50), + "PHONE_NUMBER" VARCHAR2(50), + "EMAIL" VARCHAR2(128), + PRIMARY KEY ("ID") + USING INDEX ENABLE +) ; \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/create_customer.html b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/create_customer.html new file mode 100644 index 00000000..be9e91b2 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/create_customer.html @@ -0,0 +1,148 @@ + + + + +Bank Customers Spring Boot with Oracle Autonomous AI Database + + + + + +
+
+
+
+
+

Create New Customer

+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+
+
+
+ + \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/customers.html b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/customers.html new file mode 100644 index 00000000..e85c35fc --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/customers.html @@ -0,0 +1,88 @@ + + + + +Bank Customers Spring Boot with Oracle Autonomous AI Database + + + + + + +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Customer Name Gender Is Married? Email Phone Address City State
+ Update + + Delete +
+ +
+ + \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/edit_customer.html b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/edit_customer.html new file mode 100644 index 00000000..4f5d6a74 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/main/resources/templates/edit_customer.html @@ -0,0 +1,157 @@ + + + + +Bank Customers Spring Boot with Oracle Autonomous AI Database + + + + + +
+
+
+
+
+

Update Customer

+
+
+
+ + +
+ +
+ + +
+ + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+
+
+
+ + \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/test/java/com/example/springdemoapp/SpringdemoappApplicationTests.java b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/test/java/com/example/springdemoapp/SpringdemoappApplicationTests.java new file mode 100644 index 00000000..44534e7c --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/src/test/java/com/example/springdemoapp/SpringdemoappApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.springdemoapp; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringdemoappApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/application.properties b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/application.properties new file mode 100644 index 00000000..e23420d5 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/application.properties @@ -0,0 +1,20 @@ +spring.application.name=bankcustomersapp +spring.datasource.driver-class-name=oracle.jdbc.OracleDriver +spring.datasource.url=jdbc:oracle:thin:@indeducation_high?TNS_ADMIN=/Users/Wallet_folder/ +spring.datasource.username= +spring.datasource.password= +spring.jpa.database-platform=org.hibernate.dialect.OracleDialect + +##Properties of UCP +oracle.jdbc.fanEnabled=false +spring.datasource.type=oracle.ucp.jdbc.PoolDataSource +spring.datasource.oracleucp.connection-factory-class-name=oracle.jdbc.pool.OracleDataSource +spring.datasource.oracleucp.sql-for-validate-connection=select * from dual +spring.datasource.oracleucp.connection-pool-name=connectionPoolName1 +spring.datasource.oracleucp.initial-pool-size=15 +spring.datasource.oracleucp.min-pool-size=10 +spring.datasource.oracleucp.max-pool-size=30 +##Logging properties for UCP +logging.level.root=trace +logging.file.name=logs.log +logging.level.oracle.ucp=trace \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/schema.sql b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/schema.sql new file mode 100644 index 00000000..05bc2c4a --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/schema.sql @@ -0,0 +1,17 @@ +CREATE TABLE "BANK_CUSTOMERS" +( + "ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 + MAXVALUE 99999 + INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE + NOKEEP NOSCALE NOT NULL ENABLE, + "CUSTOMER_NAME" VARCHAR2(100), + "GENDER" VARCHAR2(10), + "MARITAL_STATUS" VARCHAR2(50), + "STREET_ADDRESS" VARCHAR2(255), + "CITY" VARCHAR2(50), + "STATE" VARCHAR2(50), + "PHONE_NUMBER" VARCHAR2(50), + "EMAIL" VARCHAR2(128), + PRIMARY KEY ("ID") + USING INDEX ENABLE +) ; \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/create_customer.html b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/create_customer.html new file mode 100644 index 00000000..be9e91b2 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/create_customer.html @@ -0,0 +1,148 @@ + + + + +Bank Customers Spring Boot with Oracle Autonomous AI Database + + + + + +
+
+
+
+
+

Create New Customer

+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+
+
+
+ + \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/customers.html b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/customers.html new file mode 100644 index 00000000..e85c35fc --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/customers.html @@ -0,0 +1,88 @@ + + + + +Bank Customers Spring Boot with Oracle Autonomous AI Database + + + + + + +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Customer Name Gender Is Married? Email Phone Address City State
+ Update + + Delete +
+ +
+ + \ No newline at end of file diff --git a/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/edit_customer.html b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/edit_customer.html new file mode 100644 index 00000000..4f5d6a74 --- /dev/null +++ b/notebooks/spring-boot-app/sourcecode/BankCustomersApp/target/classes/templates/edit_customer.html @@ -0,0 +1,157 @@ + + + + +Bank Customers Spring Boot with Oracle Autonomous AI Database + + + + + +
+
+
+
+
+

Update Customer

+
+
+
+ + +
+ +
+ + +
+ + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+
+
+
+ + \ No newline at end of file diff --git a/notebooks/spring-boot-app/spring-boot-app.md b/notebooks/spring-boot-app/spring-boot-app.md new file mode 100644 index 00000000..12ce8937 --- /dev/null +++ b/notebooks/spring-boot-app/spring-boot-app.md @@ -0,0 +1,490 @@ +# Develop Spring Boot Application with Oracle Autonomous AI Database in 5 mins. + +## Introduction + +Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run". + +**Spring Boot Application Features** + +* Create stand-alone Spring applications +* Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files) +* Provide opinionated 'starter' dependencies to simplify your build configuration +* Automatically configure Spring and 3rd party libraries whenever possible +* Provide production-ready features such as metrics, health checks, and externalized configuration +* Absolutely no code generation and no requirement for XML configuration + +**Architecture** + +![SQL Run](images/architecture.png ) + +Estimated Time: 5 mins. + +### Objectives + +In this short sprint, you will: + +* Create a Spring Boot Oracle Autonomous AI Database Application +* The application will support Create, Read, Update, Delete [CRUD] operations. +* Start with a completely working Spring Boot app template and quickly create database application. + + + +### Prerequisites + +This lab assumes you have: + +* Oracle cloud account and privileges to create & manage Oracle Autonomous AI Database +* Oracle Autonomous AI database wallet has been downloaded into local filesystem. +* Source code has been downloaded. + +### Source code download + +``` + +-- Clone the GitHub Repo +gh repo clone oracle-devrel/oracle-ai-developer-hub + +Or /sourcecode folder under this markdown + + +``` + +## Task 1: Install SQL Developer and Connect to Database + +1. (Optional) Install the [VSCode extension SQL Developer](https://marketplace.visualstudio.com/items?itemName=Oracle.sql-developer), create a connection. Upload Database wallet use **Cloud Wallet** connection type + + ![Navigation](images/create-conn.png ) + +2. Create Bank Customers table, You can use **SQL Worksheets** on Autonomous Database Console or Use **SQL Developer** VSCode Extension to create this table. + + ``` + + CREATE TABLE "BANK_CUSTOMERS" + ( + "ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 + MAXVALUE 99999 + INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE + NOKEEP NOSCALE NOT NULL ENABLE, + "CUSTOMER_NAME" VARCHAR2(100), + "GENDER" VARCHAR2(10), + "MARITAL_STATUS" VARCHAR2(50), + "STREET_ADDRESS" VARCHAR2(255), + "CITY" VARCHAR2(50), + "STATE" VARCHAR2(50), + "PHONE_NUMBER" VARCHAR2(50), + "EMAIL" VARCHAR2(128), + PRIMARY KEY ("ID") + USING INDEX ENABLE + ) ; + + ``` + +## Task 2: Spring Boot Project Setup - VSCode Extensions + +1. To develop a Spring Boot application in Visual Studio Code, +you need to install the following: + + * [Java Development Kit](https://www.microsoft.com/openjdk) (JDK) + * [Extension Pack for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) + * [Spring Boot Extension Pack](https://marketplace.visualstudio.com/items?itemName=vmware.vscode-boot-dev-pack) + * Install the Extension Pack for Java + * Install the Spring Boot Extension Pack + + ![App Run](images/boottools.png ) + + ![App Run](images/bootextension.png ) + +2. Download Oracle Autonomous AI Database wallet and extract to a folder + + ![App Run](images/dbwallet.png ) + +## Task 3. Project Object Model - Add dependencies + +1. The pom.xml (Project Object Model) file in a Spring Boot project is the core configuration file used by Maven, the build automation tool. It defines the project's structure, dependencies, build process, and other essential configurations. + + Key elements of a Spring Boot pom.xml: + + * Project Metadata: This includes basic information about the project such as groupId, artifactId, version, name, and description. + * Parent POM: Spring Boot projects commonly leverage a parent POM, typically spring-boot-starter-parent. This parent POM provides default configurations for dependency management, plugins, and other settings, simplifying project setup and ensuring consistent builds. + * Dependencies: This section lists all the external libraries and frameworks required by the project. Spring Boot utilizes "starters" (e.g., spring-boot-starter-web, spring-boot-starter-data-jpa) which are convenient dependency descriptors that pull in a set of related and compatible dependencies, simplifying dependency management. + + Add the Oracle JDBC driver and UCP (Universal Connection Pool) to pom.xml: + + ``` + + + + com.oracle.database.jdbc + ojdbc11-production + 21.5.0.0 + pom + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + jakarta.persistence + jakarta.persistence-api + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + + ``` + +## Task 4. Configure the datasource in application.properties + +1. In Spring Boot, application.properties is a crucial configuration file that allows for externalized configuration of your application. It serves as a central place to define various settings and properties that control the behavior of your Spring Boot application without modifying the source code. +Key characteristics and uses of application.properties: + +2. Location: It is typically located in the **src/main/resources** directory of your Spring Boot project. + Format: It follows a simple key-value pair format, where each line represents a configuration property. + +3. Replace , , and + + + ``` + + spring.application.name=bankcustomersapp + spring.datasource.driver-class-name=oracle.jdbc.OracleDriver + + ##Change these properties -------------- + spring.datasource.url=jdbc:oracle:thin:@_high?TNS_ADMIN=//Wallets_files + spring.datasource.username= + spring.datasource.password= + ##-------------------------------------- + spring.jpa.database-platform=org.hibernate.dialect.OracleDialect + + ##Properties of UCP + oracle.jdbc.fanEnabled=false + spring.datasource.type=oracle.ucp.jdbc.PoolDataSource + spring.datasource.oracleucp.connection-factory-class-name=oracle.jdbc.pool.OracleDataSource + spring.datasource.oracleucp.sql-for-validate-connection=select * from dual + spring.datasource.oracleucp.connection-pool-name=connectionPoolName1 + spring.datasource.oracleucp.initial-pool-size=15 + spring.datasource.oracleucp.min-pool-size=10 + spring.datasource.oracleucp.max-pool-size=30 + ##Logging properties for UCP + logging.level.root=trace + logging.file.name=logs.log + logging.level.oracle.ucp=trace + + ``` + +## Task 5. Create Entity + +1. In a Spring Boot application, an entity refers to a Plain Old Java Object (POJO) class that is mapped to a table in a relational database. It is a fundamental concept in Object-Relational Mapping (ORM) using the Java Persistence API (JPA) and its implementations like Hibernate, which Spring Boot often utilizes for data access. + + * Here's a breakdown of what constitutes an entity in Spring Boot: + Database Table Representation: An entity class represents the structure of a database table. Each instance of the entity class corresponds to a row in that table. + * JPA Annotations: Entities are typically annotated with @Entity from the javax.persistence package. This annotation marks the class as a persistent entity that JPA should manage. + Table and Column Mapping: + * The @Table annotation (optional) can be used to explicitly specify the name of the database table if it differs from the class name. + Fields within the entity class represent the columns of the database table. The @Column annotation (optional) can be used to specify column details like name, length, and nullability. + +2. Map Table name, Getter and Setter methods for database columns + + ``` + + ## Refer Customer.java in the downloaded source codes + + @Entity + @Table(name = "BANK_CUSTOMERS") + + public class Customer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "CUSTOMER_NAME") + private String customerName; + + .. + + public Customer() {} + + public Customer(String customerName, String gender, String maritalStatus, String streetAddress, String city, + String state, String phoneNumber, String email) { + this.customerName = customerName; + this.gender = gender; + this.maritalStatus = maritalStatus; + this.streetAddress = streetAddress; + this.city = city; + this.state = state; + this.phoneNumber = phoneNumber; + this.email = email; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + ... + + } + + ``` + + +## Task 6. Create Interface Customer Repository which extends JpaRepository + +1. The repository is responsible for providing data access operations on the entity, + such as saving, updating, retrieving, and deleting data. + It abstracts the database operations and provides a simplified interface for interacting with the data layer + + **JpaRepository** is a core interface within Spring Data Java Persistence API, designed to simplify data access and persistence operations in Java applications using the **Java Persistence API** (JPA). It acts as a powerful abstraction layer, significantly reducing the amount of boilerplate code required for common database interactions. + + ``` + + public interface CustomerRepository extends JpaRepository { } + + ``` + +## Task 7. Create Customer Service Interface & Implementation + +1. A "Spring Boot service" refers to a component within a Spring Boot application that encapsulates and handles the business logic of the application. These services are typically marked with the @Service annotation in Java, indicating their role in the application's architecture. +Here's a breakdown of what a Spring Boot service entails: + + * Business Logic Container: Services are designed to contain the core business rules and operations. This includes tasks like data validation, complex calculations, orchestration of multiple data access operations, and interaction with other services. + * Separation of Concerns: By placing business logic in service classes, Spring Boot promotes a clean separation of concerns within the application's layers. This means: + * Controllers: (presentation layer) handle incoming requests and delegate to services. + * Services: (business logic layer) perform the actual work and interact with repositories. + * Repositories: (data access layer) handle interactions with the database. + * Dependency Injection: Spring Boot's dependency injection mechanism (often using @Autowired) allows services to easily receive and utilize other components, such as repositories or other services, without needing to manually create instances. + + ``` + + public interface CustomerService { + + List getAllCustomers(); + + Customer saveCustomer(Customer customer); + + Customer getCustomerById(Long id); + + Customer updateCustomer(Customer customer); + + void deleteCustomerById(Long id); + } + + ``` + +2. The **service implementation class** implements the Bank Customer interface (CustomerRepository) +and provides the actual implementation of the business logic + + ``` + + @Service + public class CustomerServiceImpl implements CustomerService { + + private CustomerRepository customerRepository; + + public CustomerServiceImpl(CustomerRepository customerRepository) { + super(); + this.customerRepository = customerRepository; + } + + @Override + public List getAllCustomers() { + return customerRepository.findAll(); + } + + @Override + public Customer saveCustomer(Customer customer) { + return customerRepository.save(customer); + } + @Override + public Customer getCustomerById(Long id) { + return customerRepository.findById(id).get(); + } + + @Override + public Customer updateCustomer(Customer customer) { + return customerRepository.save(customer); + } + + @Override + public void deleteCustomerById(Long id) { + customerRepository.deleteById(id); + } + + } + + ``` + +## Task 8. Create View Components + +1. In Spring Boot, a "view component" refers to a server-side component designed to encapsulate a specific piece of UI, including its associated logic and data. This approach promotes reusability, testability, and a more organized structure for building web applications, particularly when using technologies like Thymeleaf or HTMX. + +2. Create **customers.html** to list all the customers + + ``` + + + + + + + + + + + + Update + + Delete + + + + + ``` + +3. Add **create_customer.html** to add new customers (insert new records) + + + ``` + +
+
+ + +
+ +
+ + +
+ + .... + + +
+
+ ``` + +4. Add **edit_customer.html** to add update customer (Update record by ID) + + ``` + +
+
+ + +
+ + ..... + + +
+
+ ``` + +## Task 9. Create Spring Boot Main Application & Run the App + +1. The **@SpringBootApplication** annotation in Spring Boot is a convenience annotation that combines three commonly used annotations, simplifying the setup of a Spring Boot application. It is typically placed on the main class of a Spring Boot application. +2. The **@SpringBootApplication** annotation effectively combines the following: +@Configuration: This annotation tags the class as a source of bean definitions for the application context. It indicates that the class can declare one or more @Bean methods, which are processed by the Spring container to generate bean definitions. + + ``` + + @SpringBootApplication + public class BankcustomersApplication { + + public static void main(String[] args) { + SpringApplication.run(BankcustomersApplication.class, args); + } + + } + + ``` + +3. Run the application and access the app in the browser URL http://localhost:8080/customers + + ![App Run](images/runapp.png ) + + The equivalant command in terminial is as follows. + + ``` + + /usr/bin/env //.vscode/extensions/redhat.java-1.49.0-darwin-arm64/jre/21.0.9-macosx-aarch64/bin/java @/var/folders/kh/3j4qcvcn5klcg32c_xfddb900000gn/T/cp_cmbtusz8ckfsuwclwsfy4tmsn.argfile com.example.BankcustomersApplication + + ``` + +4. Running Application, View all Bank Customers + + ![App Run](images/viewall.png ) + +5. Add new Customer record + + ![App Run](images/addnew.png ) + +5. View newly added Customer + + ![App Run](images/added.png ) + +6. Update customer record and view the update + + ![App Run](images/update1.png ) + +7. View the updated customer + + ![App Run](images/update2.png ) + +## Learn More & Downloads + +* [Autonomous AI Database for Developers](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/autonomous-database-for-developers.html) +* [Download Source code](https://github.com/madhusudhanrao-ppm/dbdevrel/tree/main/source-codes) +* [Direct ORM deployment link](https://cloud.oracle.com/resourcemanager/stacks/create?zipUrl=https://objectstorage.us-phoenix-1.oraclecloud.com/p/jtfUsV33KtLR937hWybAgrq8qtuQQuAaIw1K_VBThhlUF6Z1HYF0Ai50sQlp06bQ/n/oradbclouducm/b/medical_transcripts/o/Terraform/oracle-lakehouse-devedition-stack.zip) + +## Author + +Madhusudhan Rao, Principal Product Manager, Oracle Database +Dec 2025 \ No newline at end of file