Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions private-ai-services-container/introduction/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Introduction

## About this Workshop

Welcome to **Build multimodal AI Vector Search using Oracle Private AI Service Container**.

Oracle Private AI Services Container exists to give you modern model inference inside your own environment. You get a local model endpoint without sending your text or images to a public AI service.

Its core value is control with flexibility:
- Keep **data inside your network** and security boundary
- Update model services **without changing core database** deployment
- **Reuse one model endpoint** across notebooks, SQL flows, and apps
- **Support multimodal patterns**, such as image and text embeddings in one solution

Use each path for a different job:
- **In-database embeddings (`provider=database`)** fit SQL-first workflows with minimal moving parts.
- **Private AI Services Container (`provider=privateai`)** fits teams that need model agility, multimodal use cases, or shared model serving across tools.

Compared with public embedding APIs, a private container is often the stronger enterprise choice:
- Sensitive data does not leave your environment
- Latency and cost are more predictable on local network paths
- Development is less exposed to external quotas, endpoint drift, and service outages

In the following labs you will work not only with in-database embedding but **specifically** with the Oracle Private AI Services Container to:
- discover available models in the Oracle Private AI Services Container
- generate embeddings using ONXX models stored in the Oracle AI Database and via the API endpoint provided by the Oracle Private AI Services Container.
- store vectors in Oracle AI Database
- run cosine similarity search
- build a simple image search app that used multimodal embedding models

Estimated Workshop Time: 90 minutes

### Architecture at a Glance

- `jupyterlab` runs Python notebooks.
- `privateai` serves embedding models at `http://privateai:8080` on the container network.
- `aidbfree` stores documents and vectors.

![architecture](./images/arch.png)


### Objectives

In this workshop, you will:
- Validate the runtime services required for the lab
- Generate embeddings with both database-stored ONNX models and Oracle Private AI Services Container
- Perform semantic similarity search in Oracle AI Database 26ai
- Build a simple image app that uses multimodal embeddings for similarity search


## Learn More

- [Oracle Private AI Services Container User Guide](https://docs.oracle.com/en/database/oracle/oracle-database/26/prvai/oracle-private-ai-services-container.html)
- [Private AI Services Container API Reference](https://docs.oracle.com/en/database/oracle/oracle-database/26/prvai/private-ai-services-container-api-reference.html)
- [DBMS_VECTOR UTL_TO_EMBEDDING](https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/utl_to_embedding-and-utl_to_embeddings-dbms_vector.html)

## Acknowledgements
- **Author** - Oracle LiveLabs Team
- **Last Updated By/Date** - Oracle LiveLabs Team, March 2026
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Lab 1: Verify the Runtime Environment

## Introduction

In this lab you verify that all the Oracle Private AI Services API endpoint is reachable on the netwokk.
You will also learn how to list all the models available in the container.
All checks are executed from a **JupyterLab Terminal**.

Estimated Time: 10 minutes

### Objectives

In this lab, you will:
- Verify internal container DNS resolution from JupyterLab
- Validate Private AI health and model list
- Confirm Oracle AI Database and ORDS reachability from JupyterLab
- Confirm `/home/.env` is available to the notebook

### Prerequisites

This lab assumes:
- You can open a terminal in JupyterLab (`File` -> `New` -> `Terminal`)

## Task 1: Verify Internal Hostname Resolution

1. In JupyterLab, open a new terminal.

2. Verify that runtime service names resolve:

```bash
<copy>getent hosts privateai aidbfree ords</copy>
```

Expected: one IP entry for each service name.

## Task 2: Validate Private AI REST Endpoints and list available models

1. Health endpoint:

```bash
<copy>curl -sS -i http://privateai:8080/health</copy>
```

Expected: HTTP `200`.

2. List deployed models:

```bash
<copy>curl -sS http://privateai:8080/v1/models | jq .</copy>
```

Expected: JSON payload with a `data` array of model IDs.


## Acknowledgements
- **Author** - Oracle LiveLabs Team
- **Last Updated By/Date** - Oracle LiveLabs Team, March 2026
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Notebook B: Database-Stored Model Embeddings\n",
"\n",
"This notebook demonstrates vector search using embedding models already stored in Oracle AI Database (for example `ALL_MINILM_L12_V2`).\n",
"\n",
"Flow:\n",
"- discover available database models\n",
"- generate embeddings with `provider=database`\n",
"- store vectors and run similarity search\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import json\n",
"import re\n",
"import oracledb\n",
"from dotenv import dotenv_values\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1) Load DB configuration from `/home/.env`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ENV_PATH = os.getenv('LAB_ENV_FILE', '/home/.env')\n",
"env = dotenv_values(ENV_PATH) if os.path.exists(ENV_PATH) else {}\n",
"\n",
"DB_USER = os.getenv('DB_USER') or env.get('USERNAME') or 'ADMIN'\n",
"DB_PASSWORD = os.getenv('DB_PASSWORD') or env.get('DBPASSWORD')\n",
"DB_HOST = os.getenv('DB_HOST', 'aidbfree')\n",
"DB_PORT = os.getenv('DB_PORT', '1521')\n",
"DB_SERVICE = os.getenv('DB_SERVICE', 'FREEPDB1')\n",
"DB_DSN = os.getenv('DB_DSN', f'{DB_HOST}:{DB_PORT}/{DB_SERVICE}')\n",
"PREFERRED_DB_MODEL = os.getenv('DB_EMBED_MODEL', 'ALL_MINILM_L12_V2').upper()\n",
"\n",
"print('ENV file:', ENV_PATH if os.path.exists(ENV_PATH) else 'not found')\n",
"print('DB user:', DB_USER)\n",
"print('DB dsn :', DB_DSN)\n",
"print('Preferred DB model:', PREFERRED_DB_MODEL)\n",
"\n",
"if not DB_PASSWORD:\n",
" raise ValueError('DB password not found. Set DB_PASSWORD or DBPASSWORD in /home/.env')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2) Connect and discover stored models"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"conn = oracledb.connect(user=DB_USER, password=DB_PASSWORD, dsn=DB_DSN)\n",
"cur = conn.cursor()\n",
"\n",
"cur.execute('select user from dual')\n",
"print('Connected as:', cur.fetchone()[0])\n",
"\n",
"cur.execute('''\n",
" SELECT model_name\n",
" FROM user_mining_models\n",
" ORDER BY model_name\n",
"''')\n",
"models = [row[0] for row in cur.fetchall()]\n",
"print('Models in USER_MINING_MODELS:', models)\n",
"\n",
"if not models:\n",
" raise RuntimeError('No models found in USER_MINING_MODELS. Provision an ONNX model first.')\n",
"\n",
"MODEL_NAME = PREFERRED_DB_MODEL if PREFERRED_DB_MODEL in models else models[0]\n",
"print('Selected DB model:', MODEL_NAME)\n",
"\n",
"if not re.match(r'^[A-Z][A-Z0-9_$#]*$', MODEL_NAME):\n",
" raise ValueError(f'Unsafe model identifier: {MODEL_NAME}')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3) Determine embedding dimension from the DB model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"db_params = json.dumps({\n",
" 'provider': 'database',\n",
" 'model': MODEL_NAME,\n",
"})\n",
"\n",
"cur.execute('''\n",
" SELECT VECTOR_DIMENSION_COUNT(\n",
" DBMS_VECTOR.UTL_TO_EMBEDDING(:txt, JSON(:params))\n",
" )\n",
" FROM dual\n",
"''', {'txt': 'dimension probe', 'params': db_params})\n",
"\n",
"EMBEDDING_DIM = int(cur.fetchone()[0])\n",
"print('Embedding dimension:', EMBEDDING_DIM)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4) Create table and store in-database embeddings"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"TABLE_NAME = 'PRIVATEAI_DOCS_DBMODEL'\n",
"\n",
"try:\n",
" cur.execute(f'DROP TABLE {TABLE_NAME} PURGE')\n",
"except oracledb.DatabaseError:\n",
" pass\n",
"\n",
"cur.execute(f'''\n",
" CREATE TABLE {TABLE_NAME} (\n",
" doc_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n",
" title VARCHAR2(200) NOT NULL,\n",
" content CLOB NOT NULL,\n",
" embedding VECTOR({EMBEDDING_DIM}, FLOAT32)\n",
" )\n",
"''')\n",
"\n",
"docs = [\n",
" ('Database Model Path', 'Embeddings can be generated directly in Oracle AI Database with a stored ONNX model.'),\n",
" ('Operational Simplicity', 'Keeping model inference in-database can simplify architecture and governance.'),\n",
" ('Vector Search SQL', 'Use VECTOR_DISTANCE with COSINE to rank semantic similarity results.'),\n",
" ('Model Governance', 'Database-stored models can be versioned and controlled with DB privileges.'),\n",
"]\n",
"\n",
"inserted = 0\n",
"for title, content in docs:\n",
" cur.execute(f'''\n",
" INSERT INTO {TABLE_NAME} (title, content, embedding)\n",
" VALUES (\n",
" :title,\n",
" :content,\n",
" DBMS_VECTOR.UTL_TO_EMBEDDING(:content, JSON(:params))\n",
" )\n",
" ''', {'title': title, 'content': content, 'params': db_params})\n",
" inserted += 1\n",
"\n",
"conn.commit()\n",
"print('Inserted rows:', inserted)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5) Similarity search with the database model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"query_text = 'Which approach keeps embedding generation inside Oracle Database?'\n",
"\n",
"sql = f'''\n",
"SELECT\n",
" title,\n",
" ROUND(1 - VECTOR_DISTANCE(\n",
" embedding,\n",
" DBMS_VECTOR.UTL_TO_EMBEDDING(:query_text, JSON(:params)),\n",
" COSINE\n",
" ), 4) AS similarity,\n",
" SUBSTR(content, 1, 120) AS preview\n",
"FROM {TABLE_NAME}\n",
"ORDER BY VECTOR_DISTANCE(\n",
" embedding,\n",
" DBMS_VECTOR.UTL_TO_EMBEDDING(:query_text, JSON(:params)),\n",
" COSINE\n",
")\n",
"FETCH FIRST 3 ROWS ONLY\n",
"'''\n",
"\n",
"cur.execute(sql, {'query_text': query_text, 'params': db_params})\n",
"rows = cur.fetchall()\n",
"\n",
"print('Query:', query_text)\n",
"for idx, (title, score, preview) in enumerate(rows, 1):\n",
" print(f'{idx}. {title} | score={score}')\n",
" print(f' {preview}')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Optional cleanup\n",
"# cur.execute(f'DROP TABLE {TABLE_NAME} PURGE')\n",
"# conn.commit()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cur.close()\n",
"conn.close()\n",
"print('Connection closed.')\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Binary file not shown.
Loading