From d57cc778e8e91186c84bdda25c8d843479b206b4 Mon Sep 17 00:00:00 2001 From: Daniela Pavlenco Date: Wed, 7 Oct 2026 13:42:05 +0300 Subject: [PATCH] Remove obsolete LangChain JS RAG notebook --- ...chain_js_rag_with_oracle_ai_database.ipynb | 2244 ----------------- 1 file changed, 2244 deletions(-) delete mode 100644 notebooks/langchain_ecosystem/langchain_js_rag_with_oracle_ai_database.ipynb diff --git a/notebooks/langchain_ecosystem/langchain_js_rag_with_oracle_ai_database.ipynb b/notebooks/langchain_ecosystem/langchain_js_rag_with_oracle_ai_database.ipynb deleted file mode 100644 index 65bfe2e6..00000000 --- a/notebooks/langchain_ecosystem/langchain_js_rag_with_oracle_ai_database.ipynb +++ /dev/null @@ -1,2244 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "accab619", - "metadata": {}, - "source": [ - "# LangChain.js RAG with Oracle AI Database\n", - "\n", - "This notebook shows how to build a JavaScript RAG workflow with LangChain.js and Oracle AI Database.\n", - "\n", - "It focuses on the TypeScript/JavaScript integration package `@oracle/langchain-oracledb`. The runnable notebook uses JavaScript modules so it can execute directly with Node.js, and the same imports can be used from TypeScript with a normal `tsconfig` build.\n", - "\n", - "**What this notebook demonstrates**\n", - "\n", - "- Connect from Node.js to FreeSQL, Autonomous AI Database, or local Oracle Database.\n", - "- Validate the schema before creating vector-store objects.\n", - "- Store documents and metadata with `OracleVS`.\n", - "- Inspect the Oracle table created by the vector store.\n", - "- Run similarity search, metadata-filtered search, and MMR retrieval.\n", - "- Show where vector index creation fits when the database environment has enough quota.\n", - "- Generate a grounded RAG answer with a chat model from retrieved OracleVS context." - ] - }, - { - "cell_type": "markdown", - "id": "0f440d09", - "metadata": {}, - "source": [ - "## Architecture at a Glance\n", - "\n", - "```text\n", - " +-------------------------------+\n", - " | JavaScript / TypeScript app |\n", - " | Node.js + LangChain.js |\n", - " +---------------+---------------+\n", - " |\n", - " | uses\n", - " v\n", - " +-------------------------------+\n", - " | @oracle/langchain-oracledb |\n", - " | OracleVS vector store |\n", - " +---------------+---------------+\n", - " |\n", - " | stores and queries\n", - " v\n", - "+----------------------------------------------------------------+\n", - "| Oracle AI Database |\n", - "| VECTOR embeddings + document text + JSON metadata + indexes |\n", - "+-------------------------------+--------------------------------+\n", - " |\n", - " | returns retrieved context\n", - " v\n", - " +-------------------------------+\n", - " | Grounded RAG response |\n", - " +-------------------------------+\n", - "```\n", - "\n", - "`OracleVS` is the main integration point. The application keeps the LangChain developer experience, while Oracle AI Database stores the text, embedding vectors, and metadata used for retrieval." - ] - }, - { - "cell_type": "markdown", - "id": "b361b3c7", - "metadata": {}, - "source": [ - "## What the Oracle LangChain.js Package Provides\n", - "\n", - "The Oracle JavaScript integration package connects LangChain.js applications to Oracle AI Database. In the official guide, the package surface includes vector storage, similarity search, document loading, text splitting, in-database embeddings, summarization, and vector index creation.\n", - "\n", - "This notebook focuses the runnable path on the pieces that are most useful for a first Developer Hub tutorial and work well on FreeSQL:\n", - "\n", - "| Capability | Why it matters in a RAG app | Covered in runnable cells |\n", - "| --- | --- | --- |\n", - "| `OracleVS` | Stores text, metadata, and embeddings in Oracle AI Database | Yes |\n", - "| Similarity search | Retrieves nearest chunks for a user question | Yes |\n", - "| Metadata filtering | Restricts retrieval using JSON metadata predicates | Yes |\n", - "| MMR retrieval | Balances relevance and diversity in retrieved context | Yes |\n", - "| `createIndex` | Adds HNSW/IVF vector indexes when the DB environment supports it | Opt-in |\n", - "| `OracleDocLoader`, `OracleTextSplitter`, `OracleEmbeddings`, `OracleSummary` | Useful extensions for production ingestion and Oracle-backed model calls | Explained at the end |" - ] - }, - { - "cell_type": "markdown", - "id": "0c05264d", - "metadata": {}, - "source": [ - "## Database Connection Guide\n", - "\n", - "Use one of these connection paths. The notebook reads the same `.env` variable names for all three options.\n", - "\n", - "
\n", - "FreeSQL\n", - "\n", - "Use FreeSQL when you want a hosted Oracle Database schema for a tutorial without setting up a local database.\n", - "\n", - "**Step 1: Open FreeSQL**\n", - "\n", - "Open [FreeSQL](https://freesql.com). The worksheet interface is where you can browse schema objects, run SQL, and access the database connection details.\n", - "\n", - "

\n", - " \"FreeSQL\n", - "

\n", - "
\n", - "\n", - "**Step 2: Sign in**\n", - "\n", - "Select **Sign In** and authenticate with your Oracle account, or create an Oracle account if you do not already have one.\n", - "\n", - "

\n", - " \"Oracle\n", - "

\n", - "
\n", - "\n", - "**Step 3: Copy the NodeJS connection details**\n", - "\n", - "After sign-in, select **Connect to the Database** from the top navigation. Choose the **NodeJS** tab for this LangChain.js notebook, then copy the generated username, password, and connection string.\n", - "\n", - "

\n", - " \"FreeSQL\n", - "

\n", - "
\n", - "\n", - "**Step 4: Create the notebook `.env` file**\n", - "\n", - "Add the values to a private `.env` file in the same folder as this notebook.\n", - "\n", - "```env\n", - "DB_USER=\n", - "DB_PASSWORD=\n", - "DB_CONNECT_STRING=\n", - "LANGCHAIN_JS_TABLE=LC_JS_RAG_DEMO\n", - "LANGCHAIN_JS_CREATE_INDEX=false\n", - "```\n", - "\n", - "FreeSQL normally works for this notebook path because it uses regular schema tables. Vector index creation is disabled by default because small hosted schemas may not have enough quota for index auxiliary objects.\n", - "\n", - "
\n", - "\n", - "
\n", - "Autonomous AI Database\n", - "\n", - "Use Autonomous AI Database when you want the same LangChain.js workflow against a managed Oracle AI Database environment.\n", - "\n", - "```env\n", - "DB_USER=\n", - "DB_PASSWORD=\n", - "DB_CONNECT_STRING=\n", - "LANGCHAIN_JS_TABLE=LC_JS_RAG_DEMO\n", - "LANGCHAIN_JS_CREATE_INDEX=true\n", - "```\n", - "\n", - "
\n", - "\n", - "
\n", - "Local Oracle Database\n", - "\n", - "Use a local Oracle Database when you want a repeatable development environment on your machine or in a container.\n", - "\n", - "```env\n", - "DB_USER=\n", - "DB_PASSWORD=\n", - "DB_CONNECT_STRING=localhost:1521/\n", - "LANGCHAIN_JS_TABLE=LC_JS_RAG_DEMO\n", - "LANGCHAIN_JS_CREATE_INDEX=true\n", - "```\n", - "\n", - "
\n" - ] - }, - { - "cell_type": "markdown", - "id": "054f707f", - "metadata": {}, - "source": [ - "## 1. Prepare the JavaScript Workspace" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "9e1e8db2", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:54:47.842468Z", - "iopub.status.busy": "2026-09-23T12:54:47.842468Z", - "iopub.status.idle": "2026-09-23T12:54:47.855941Z", - "shell.execute_reply": "2026-09-23T12:54:47.855402Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "JavaScript runtime ready.\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "import json\n", - "import os\n", - "import subprocess\n", - "import tempfile\n", - "\n", - "NOTEBOOK_DIR = Path.cwd()\n", - "RUNTIME_DIR = Path(tempfile.mkdtemp(prefix=\"langchain-js-oracle-\"))\n", - "NPM_BIN = \"npm.cmd\" if os.name == \"nt\" else \"npm\"\n", - "RUN_ENV = os.environ.copy()\n", - "RUN_ENV[\"NOTEBOOK_ENV_PATH\"] = str(NOTEBOOK_DIR / \".env\")\n", - "\n", - "print(\"JavaScript runtime ready.\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "d7118c0b", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:54:47.860097Z", - "iopub.status.busy": "2026-09-23T12:54:47.860097Z", - "iopub.status.idle": "2026-09-23T12:54:47.867345Z", - "shell.execute_reply": "2026-09-23T12:54:47.866264Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Node.js package manifest ready.\n" - ] - } - ], - "source": [ - "package_json = {\n", - " \"name\": \"langchain-js-oracle-ai-database-rag\",\n", - " \"version\": \"1.0.0\",\n", - " \"private\": True,\n", - " \"type\": \"module\",\n", - " \"scripts\": {\n", - " \"demo\": \"node oracle_langchain_rag_demo.mjs\"\n", - " },\n", - " \"dependencies\": {\n", - " \"@langchain/core\": \"^1.0.0\",\n", - " \"@langchain/openai\": \"^1.0.0\",\n", - " \"@oracle/langchain-oracledb\": \"^1.0.0\",\n", - " \"dotenv\": \"^17.2.3\",\n", - " \"oracledb\": \"^6.10.0\"\n", - " }\n", - "}\n", - "\n", - "_ = (RUNTIME_DIR / \"package.json\").write_text(json.dumps(package_json, indent=2), encoding=\"utf-8\")\n", - "print(\"Node.js package manifest ready.\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "cde3c7e3", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:54:47.869000Z", - "iopub.status.busy": "2026-09-23T12:54:47.869000Z", - "iopub.status.idle": "2026-09-23T12:55:02.619720Z", - "shell.execute_reply": "2026-09-23T12:55:02.619720Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Node.js dependencies installed.\n" - ] - } - ], - "source": [ - "result = subprocess.run(\n", - " [NPM_BIN, \"install\"],\n", - " cwd=RUNTIME_DIR,\n", - " env=RUN_ENV,\n", - " text=True,\n", - " encoding=\"utf-8\",\n", - " errors=\"replace\",\n", - " capture_output=True,\n", - ")\n", - "\n", - "if result.returncode != 0:\n", - " print(result.stdout[-3000:])\n", - " print(result.stderr[-3000:])\n", - " raise RuntimeError(\"npm install failed. Check the npm output above.\")\n", - "\n", - "print(\"Node.js dependencies installed.\")" - ] - }, - { - "cell_type": "markdown", - "id": "029845c8", - "metadata": {}, - "source": [ - "## 2. Configure the Connection Profile\n", - "\n", - "Create a private `.env` file in the same folder as this notebook. The notebook uses these values from JavaScript through `dotenv`, but it never prints the credentials.\n", - "\n", - "```env\n", - "DB_USER=\n", - "DB_PASSWORD=\n", - "DB_CONNECT_STRING=\n", - "LANGCHAIN_JS_TABLE=LC_JS_RAG_DEMO\n", - "LANGCHAIN_JS_CREATE_INDEX=false\n", - "\n", - "# Required for the final RAG generation step.\n", - "# Use OPENAI_API_KEY, or keep MODEL_PROVIDER_API_KEY if your environment already uses that name.\n", - "OPENAI_API_KEY=\n", - "MODEL_PROVIDER_API_KEY=\n", - "LANGCHAIN_JS_LLM_MODEL=gpt-4o-mini\n", - "```\n", - "\n", - "For FreeSQL, use **Connect to the Database** and choose the **NodeJS** tab for this LangChain.js notebook. Copy the generated username, password, and connection string into the private `.env` file. The next executable step validates the connection and prints only the database target, for example `Connected to FreeSQL`.\n", - "\n", - "The final RAG section sends the retrieved OracleVS context to the configured chat model. If your private `.env` already has `MODEL_PROVIDER_API_KEY`, the JavaScript code will use it automatically; otherwise set `OPENAI_API_KEY`.\n" - ] - }, - { - "cell_type": "markdown", - "id": "3a1cb688", - "metadata": {}, - "source": [ - "## 3. Create the Database Connection Module\n", - "\n", - "This JavaScript module loads the private `.env` file, sets the notebook program identifier, and exposes a reusable `node-oracledb` pool factory. It is the only place that reads credentials." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "66f379c3", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:02.619720Z", - "iopub.status.busy": "2026-09-23T12:55:02.619720Z", - "iopub.status.idle": "2026-09-23T12:55:02.628766Z", - "shell.execute_reply": "2026-09-23T12:55:02.628766Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Database connection module ready.\n" - ] - } - ], - "source": [ - "db_config_js = r'''\n", - "import fs from \"node:fs\";\n", - "import path from \"node:path\";\n", - "import process from \"node:process\";\n", - "\n", - "import dotenv from \"dotenv\";\n", - "import oracledb from \"oracledb\";\n", - "\n", - "const runtimeDir = process.cwd();\n", - "dotenv.config({ path: process.env.NOTEBOOK_ENV_PATH || path.resolve(runtimeDir, \"..\", \".env\") });\n", - "\n", - "if (oracledb.defaults) {\n", - " oracledb.defaults.program = \"devrel-developerhub-langchain-js-oracle-rag\";\n", - "}\n", - "\n", - "export const tableName = (process.env.LANGCHAIN_JS_TABLE || \"LC_JS_RAG_DEMO\").toUpperCase();\n", - "export const createVectorIndex = (process.env.LANGCHAIN_JS_CREATE_INDEX || \"false\").toLowerCase() === \"true\";\n", - "export const resultsPath = path.join(runtimeDir, \"rag_results.json\");\n", - "export const llmApiKey = process.env.OPENAI_API_KEY || process.env.MODEL_PROVIDER_API_KEY;\n", - "export const llmModel = process.env.LANGCHAIN_JS_LLM_MODEL || process.env.OPENAI_MODEL || process.env.OAMP_LLM_MODEL || \"gpt-4o-mini\";\n", - "\n", - "const dbUser = process.env.DB_USER || process.env.ORACLEDB_USER;\n", - "const dbPassword = process.env.DB_PASSWORD || process.env.ORACLEDB_PASSWORD;\n", - "const dbConnectString =\n", - " process.env.DB_CONNECT_STRING ||\n", - " process.env.DB_DSN ||\n", - " process.env.ORACLEDB_CONNECTION_STRING;\n", - "\n", - "const missing = [\n", - " [\"DB_USER\", dbUser],\n", - " [\"DB_PASSWORD\", dbPassword],\n", - " [\"DB_CONNECT_STRING or DB_DSN\", dbConnectString],\n", - "]\n", - " .filter(([, value]) => !value)\n", - " .map(([name]) => name);\n", - "\n", - "if (missing.length > 0) {\n", - " throw new Error(`Missing required environment variables: ${missing.join(\", \")}`);\n", - "}\n", - "\n", - "export function writeResults(payload) {\n", - " fs.writeFileSync(resultsPath, JSON.stringify(payload, null, 2));\n", - "}\n", - "\n", - "export async function createPool() {\n", - " return oracledb.createPool({\n", - " user: dbUser,\n", - " password: dbPassword,\n", - " connectString: dbConnectString,\n", - " });\n", - "}\n", - "'''\n", - "\n", - "_ = (RUNTIME_DIR / \"db_config.mjs\").write_text(db_config_js, encoding=\"utf-8\")\n", - "print(\"Database connection module ready.\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "fde5d9a6", - "metadata": {}, - "source": [ - "## 4. Add Database Preflight Checks\n", - "\n", - "Before using `OracleVS`, the notebook verifies that the schema can create, insert into, select from, and drop normal application tables. This keeps FreeSQL failures easy to diagnose." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "2fa6c080", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:02.628766Z", - "iopub.status.busy": "2026-09-23T12:55:02.628766Z", - "iopub.status.idle": "2026-09-23T12:55:02.635580Z", - "shell.execute_reply": "2026-09-23T12:55:02.635580Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Database preflight checks ready.\n" - ] - } - ], - "source": [ - "db_preflight_js = r\"\"\"\n", - "export async function tableExists(connection, name) {\n", - " const result = await connection.execute(\n", - " `SELECT COUNT(*) FROM user_tables WHERE table_name = :name`,\n", - " [name.toUpperCase()],\n", - " );\n", - " return Number(result.rows[0][0]) > 0;\n", - "}\n", - "\n", - "export async function runTablePreflight(connection, summary) {\n", - " const diagTable = \"LC_JS_PREFLIGHT\";\n", - " try {\n", - " await connection.execute(`DROP TABLE ${diagTable} PURGE`);\n", - " } catch (error) {\n", - " if (!String(error.message).includes(\"ORA-00942\")) throw error;\n", - " }\n", - "\n", - " await connection.execute(`CREATE TABLE ${diagTable} (id NUMBER PRIMARY KEY, note VARCHAR2(100))`);\n", - " await connection.execute(`INSERT INTO ${diagTable} VALUES (1, 'table permission check')`, [], { autoCommit: true });\n", - " const rows = await connection.execute(`SELECT COUNT(*) FROM ${diagTable}`);\n", - " await connection.execute(`DROP TABLE ${diagTable} PURGE`);\n", - "\n", - " summary.push({\n", - " step: \"table_operations\",\n", - " status: \"PASS\",\n", - " detail: `Created, inserted, selected ${rows.rows[0][0]} row, and dropped a test table`,\n", - " });\n", - "}\n", - "\n", - "export async function checkTablespaceQuota(connection) {\n", - " const result = await connection.execute(\n", - " `SELECT tablespace_name, bytes, max_bytes FROM user_ts_quotas WHERE tablespace_name = 'USERS'`,\n", - " );\n", - "\n", - " if (!result.rows || result.rows.length === 0) {\n", - " return { status: \"UNKNOWN\", detail: \"No USERS quota row returned for this schema\" };\n", - " }\n", - "\n", - " const [tablespaceName, usedBytes, maxBytes] = result.rows[0];\n", - " if (Number(maxBytes) < 0) {\n", - " return { status: \"PASS\", detail: `${tablespaceName}: unlimited quota` };\n", - " }\n", - "\n", - " const freeBytes = Number(maxBytes) - Number(usedBytes);\n", - " const usedMb = Number(usedBytes) / 1024 / 1024;\n", - " const maxMb = Number(maxBytes) / 1024 / 1024;\n", - " const freeMb = freeBytes / 1024 / 1024;\n", - "\n", - " if (freeBytes < 2_500_000) {\n", - " return {\n", - " status: \"MISSING\",\n", - " detail: `${tablespaceName}: ${usedMb.toFixed(2)} MB used of ${maxMb.toFixed(2)} MB; only ${freeMb.toFixed(2)} MB remains. Use a fresh FreeSQL schema or drop old demo objects before creating the OracleVS table.`,\n", - " };\n", - " }\n", - "\n", - " return {\n", - " status: \"PASS\",\n", - " detail: `${tablespaceName}: ${usedMb.toFixed(2)} MB used of ${maxMb.toFixed(2)} MB; ${freeMb.toFixed(2)} MB remains`,\n", - " };\n", - "}\n", - "\"\"\"\n", - "\n", - "_ = (RUNTIME_DIR / \"db_preflight.mjs\").write_text(db_preflight_js, encoding=\"utf-8\")\n", - "print(\"Database preflight checks ready.\")" - ] - }, - { - "cell_type": "markdown", - "id": "1c0ac408", - "metadata": {}, - "source": [ - "## 5. Add Vector Table Inspection\n", - "\n", - "After `OracleVS` runs, this helper reads Oracle data dictionary views so the notebook can show what table, columns, and indexes were created." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "d115d1f6", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:02.635580Z", - "iopub.status.busy": "2026-09-23T12:55:02.635580Z", - "iopub.status.idle": "2026-09-23T12:55:02.642628Z", - "shell.execute_reply": "2026-09-23T12:55:02.642628Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Vector table inspection ready.\n" - ] - } - ], - "source": [ - "db_inspection_js = r\"\"\"\n", - "export async function inspectVectorTable(connection, name) {\n", - " const tableCount = await connection.execute(`SELECT COUNT(*) FROM ${name}`);\n", - " const columnRows = await connection.execute(\n", - " `SELECT column_name, data_type\n", - " FROM user_tab_columns\n", - " WHERE table_name = :tableName\n", - " ORDER BY column_id`,\n", - " [name],\n", - " );\n", - " const indexRows = await connection.execute(\n", - " `SELECT index_name, index_type\n", - " FROM user_indexes\n", - " WHERE table_name = :tableName\n", - " ORDER BY index_name`,\n", - " [name],\n", - " );\n", - " const storageRows = await connection.execute(\n", - " `SELECT ROUND(SUM(bytes)/1024/1024, 3) AS mb\n", - " FROM user_segments\n", - " WHERE segment_name = :tableName`,\n", - " [name],\n", - " );\n", - "\n", - " return {\n", - " rowCount: Number(tableCount.rows[0][0]),\n", - " columns: columnRows.rows.map(([column, type]) => ({ column, type })),\n", - " indexes: indexRows.rows.map(([index, type]) => ({ index, type })),\n", - " storageMb: Number(storageRows.rows[0][0] || 0),\n", - " };\n", - "}\n", - "\"\"\"\n", - "\n", - "_ = (RUNTIME_DIR / \"db_inspection.mjs\").write_text(db_inspection_js, encoding=\"utf-8\")\n", - "print(\"Vector table inspection ready.\")" - ] - }, - { - "cell_type": "markdown", - "id": "9c3a7cbd", - "metadata": {}, - "source": [ - "## 6. Test the Database Connection\n", - "\n", - "Run a direct `node-oracledb` connection check before the LangChain.js workflow. This confirms that Node.js can open a database session with the `.env` values, without printing credentials." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "2d8ea6a7", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:02.642628Z", - "iopub.status.busy": "2026-09-23T12:55:02.642628Z", - "iopub.status.idle": "2026-09-23T12:55:02.649259Z", - "shell.execute_reply": "2026-09-23T12:55:02.649259Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connection check ready.\n" - ] - } - ], - "source": [ - "connection_check_js = r\"\"\"\n", - "import { createPool } from \"./db_config.mjs\";\n", - "\n", - "let pool;\n", - "let connection;\n", - "\n", - "try {\n", - " pool = await createPool();\n", - " connection = await pool.getConnection();\n", - "\n", - " const who = await connection.execute(\n", - " `SELECT\n", - " SYS_CONTEXT('USERENV', 'SERVICE_NAME') AS service_name,\n", - " SYS_CONTEXT('USERENV', 'SERVER_HOST') AS server_host\n", - " FROM dual`,\n", - " );\n", - "\n", - " const [serviceName, serverHost] = who.rows[0];\n", - " const target =\n", - " String(serverHost || \"\").toLowerCase().includes(\"freesql\") ||\n", - " String(serviceName || \"\").toLowerCase().includes(\"freesql\")\n", - " ? \"FreeSQL\"\n", - " : \"Oracle Database\";\n", - "\n", - " console.log(`Connected to ${target}`);\n", - "} finally {\n", - " if (connection) await connection.close();\n", - " if (pool) await pool.close(0);\n", - "}\n", - "\"\"\"\n", - "\n", - "_ = (RUNTIME_DIR / \"connection_check.mjs\").write_text(connection_check_js, encoding=\"utf-8\")\n", - "print(\"Connection check ready.\")" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "92459ecd", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:02.649259Z", - "iopub.status.busy": "2026-09-23T12:55:02.649259Z", - "iopub.status.idle": "2026-09-23T12:55:05.274508Z", - "shell.execute_reply": "2026-09-23T12:55:05.273863Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connected to FreeSQL\n" - ] - } - ], - "source": [ - "connection_result = subprocess.run(\n", - " [\"node\", \"connection_check.mjs\"],\n", - " cwd=RUNTIME_DIR,\n", - " env=RUN_ENV,\n", - " text=True,\n", - " encoding=\"utf-8\",\n", - " errors=\"replace\",\n", - " capture_output=True,\n", - ")\n", - "\n", - "clean_connection_output = \"\\n\".join(\n", - " line for line in connection_result.stdout.splitlines()\n", - " if line.strip() and \"injected env\" not in line\n", - ")\n", - "if clean_connection_output:\n", - " print(clean_connection_output)\n", - "\n", - "if connection_result.returncode != 0:\n", - " print(connection_result.stderr[-2000:])\n", - " raise RuntimeError(\"Database connection check failed.\")" - ] - }, - { - "cell_type": "markdown", - "id": "7eab1835", - "metadata": {}, - "source": [ - "## 7. Create the Demo Embeddings\n", - "\n", - "The demo embedding model keeps the notebook fully runnable without an external model key. It implements the standard LangChain.js `Embeddings` interface, so the rest of the workflow looks the same as it would with a production embedding provider." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "864107cd", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:05.276063Z", - "iopub.status.busy": "2026-09-23T12:55:05.276063Z", - "iopub.status.idle": "2026-09-23T12:55:05.282263Z", - "shell.execute_reply": "2026-09-23T12:55:05.282263Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Demo embedding model ready.\n" - ] - } - ], - "source": [ - "embeddings_js = r'''\n", - "import { Embeddings } from \"@langchain/core/embeddings\";\n", - "\n", - "export class DemoEmbeddings extends Embeddings {\n", - " constructor() {\n", - " super({});\n", - " this.dimensions = 12;\n", - " this.terms = [\n", - " \"oracle\",\n", - " \"database\",\n", - " \"vector\",\n", - " \"langchain\",\n", - " \"javascript\",\n", - " \"typescript\",\n", - " \"rag\",\n", - " \"metadata\",\n", - " \"freesql\",\n", - " \"agent\",\n", - " \"retrieval\",\n", - " \"enterprise\",\n", - " ];\n", - " }\n", - "\n", - " textToVector(text) {\n", - " const lower = text.toLowerCase();\n", - " const vector = this.terms.map((term) => {\n", - " const matches = lower.match(new RegExp(term, \"g\"));\n", - " return matches ? matches.length : 0;\n", - " });\n", - "\n", - " for (const token of lower.split(/[^a-z0-9]+/).filter(Boolean)) {\n", - " let bucket = 0;\n", - " for (const char of token) {\n", - " bucket = (bucket + char.charCodeAt(0)) % this.dimensions;\n", - " }\n", - " vector[bucket] += 0.15;\n", - " }\n", - "\n", - " const norm = Math.sqrt(vector.reduce((sum, value) => sum + value * value, 0)) || 1;\n", - " return vector.map((value) => Number((value / norm).toFixed(6)));\n", - " }\n", - "\n", - " async embedQuery(text) {\n", - " return this.textToVector(text);\n", - " }\n", - "\n", - " async embedDocuments(texts) {\n", - " return texts.map((text) => this.textToVector(text));\n", - " }\n", - "}\n", - "'''\n", - "\n", - "_ = (RUNTIME_DIR / \"demo_embeddings.mjs\").write_text(embeddings_js, encoding=\"utf-8\")\n", - "print(\"Demo embedding model ready.\")" - ] - }, - { - "cell_type": "markdown", - "id": "90f5f6f0", - "metadata": {}, - "source": [ - "## 8. Create the Demo Corpus\n", - "\n", - "The corpus uses short, structured documents with metadata. That lets the retrieval section demonstrate semantic search and JSON metadata filtering against the same Oracle vector table." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "e1e1909d", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:05.282263Z", - "iopub.status.busy": "2026-09-23T12:55:05.282263Z", - "iopub.status.idle": "2026-09-23T12:55:05.289604Z", - "shell.execute_reply": "2026-09-23T12:55:05.289604Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Demo corpus ready.\n" - ] - } - ], - "source": [ - "corpus_js = r'''\n", - "import { Document } from \"@langchain/core/documents\";\n", - "import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\n", - "import { ChatOpenAI } from \"@langchain/openai\";\n", - "import { llmApiKey, llmModel } from \"./db_config.mjs\";\n", - "\n", - "export function docsForDemo() {\n", - " return [\n", - " new Document({\n", - " pageContent:\n", - " \"Oracle AI Database stores vectors alongside operational data, so RAG applications can keep retrieval close to governed enterprise records.\",\n", - " metadata: { source: \"oracle-ai-database\", category: \"database\", product: \"Oracle AI Database\", priority: \"high\", audience: \"architect\" },\n", - " }),\n", - " new Document({\n", - " pageContent:\n", - " \"LangChain.js RAG applications use OracleVS with Oracle AI Database as a vector store for adding documents, preserving metadata, and running similarity search retrieval.\",\n", - " metadata: { source: \"langchain-js\", category: \"framework\", product: \"LangChain.js\", priority: \"high\", audience: \"developer\" },\n", - " }),\n", - " new Document({\n", - " pageContent:\n", - " \"FreeSQL gives developers a hosted Oracle Database schema for tutorials that only need normal schema table operations.\",\n", - " metadata: { source: \"freesql\", category: \"setup\", product: \"FreeSQL\", priority: \"medium\", audience: \"developer\" },\n", - " }),\n", - " new Document({\n", - " pageContent:\n", - " \"Metadata filters narrow OracleVS retrieval by product, source, category, tenant, priority, or other JSON metadata fields.\",\n", - " metadata: { source: \"metadata-filtering\", category: \"retrieval\", product: \"OracleVS\", priority: \"high\", audience: \"developer\" },\n", - " }),\n", - " new Document({\n", - " pageContent:\n", - " \"Maximal marginal relevance helps LangChain.js retrieve a diverse context set instead of returning several near-duplicate chunks.\",\n", - " metadata: { source: \"mmr\", category: \"retrieval\", product: \"LangChain.js\", priority: \"medium\", audience: \"developer\" },\n", - " }),\n", - " new Document({\n", - " pageContent:\n", - " \"A production RAG service can swap the demo embedding class for OpenAI, OCI Generative AI, or Oracle-backed embeddings without changing OracleVS retrieval code.\",\n", - " metadata: { source: \"embedding-provider\", category: \"architecture\", product: \"LangChain.js\", priority: \"medium\", audience: \"architect\" },\n", - " }),\n", - " new Document({\n", - " pageContent:\n", - " \"Autonomous AI Database and local Oracle Database use the same node-oracledb connection pattern when the schema can create and manage application tables.\",\n", - " metadata: { source: \"connection-options\", category: \"setup\", product: \"node-oracledb\", priority: \"medium\", audience: \"developer\" },\n", - " }),\n", - " ];\n", - "}\n", - "\n", - "export function docRow(doc, distance, rank) {\n", - " return {\n", - " rank,\n", - " distance: distance === undefined ? null : Number(distance.toFixed(6)),\n", - " source: doc.metadata.source,\n", - " product: doc.metadata.product,\n", - " category: doc.metadata.category,\n", - " snippet: doc.pageContent,\n", - " };\n", - "}\n", - "\n", - "export function buildContext(rows) {\n", - " return rows\n", - " .map((row) => `[${row.rank}] source=${row.source}; product=${row.product}; category=${row.category}; snippet=${row.snippet}`)\n", - " .join(\"\\n\");\n", - "}\n", - "\n", - "function contentToText(content) {\n", - " if (typeof content === \"string\") return content;\n", - " if (Array.isArray(content)) {\n", - " return content\n", - " .map((part) => {\n", - " if (typeof part === \"string\") return part;\n", - " if (part && typeof part === \"object\" && \"text\" in part) return part.text;\n", - " return JSON.stringify(part);\n", - " })\n", - " .join(\"\");\n", - " }\n", - " return String(content ?? \"\");\n", - "}\n", - "\n", - "export async function buildAnswer(question, rows) {\n", - " if (!llmApiKey) {\n", - " throw new Error(\"Missing OPENAI_API_KEY or MODEL_PROVIDER_API_KEY. Set one of these values in .env to run the RAG generation step.\");\n", - " }\n", - "\n", - " const context = buildContext(rows);\n", - " const model = new ChatOpenAI({\n", - " apiKey: llmApiKey,\n", - " model: llmModel,\n", - " temperature: 0,\n", - " });\n", - "\n", - " const response = await model.invoke([\n", - " new SystemMessage(\n", - " \"You are a concise technical assistant. Answer only from the retrieved context. If the context is insufficient, say what is missing.\"\n", - " ),\n", - " new HumanMessage(\n", - " `Question:\\n${question}\\n\\nRetrieved context:\\n${context}\\n\\nWrite a grounded answer in 3-5 sentences and mention the Oracle/LangChain components that support the answer.`\n", - " ),\n", - " ]);\n", - "\n", - " return [\n", - " `Question: ${question}`,\n", - " \"\",\n", - " `Grounded answer generated by ${llmModel}:`,\n", - " contentToText(response.content).trim(),\n", - " \"\",\n", - " \"Retrieved context used:\",\n", - " context,\n", - " ].join(\"\\n\");\n", - "}\n", - "'''\n", - "\n", - "_ = (RUNTIME_DIR / \"demo_corpus.mjs\").write_text(corpus_js, encoding=\"utf-8\")\n", - "print(\"Demo corpus ready.\")" - ] - }, - { - "cell_type": "markdown", - "id": "eabc2506", - "metadata": {}, - "source": [ - "## 9. Build the OracleVS Setup Step\n", - "\n", - "This module creates a LangChain.js `OracleVS` store from the demo documents. It also resets the demo table on rerun, checks tablespace quota, and optionally creates an HNSW vector index." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "22a4b8b6", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:05.292106Z", - "iopub.status.busy": "2026-09-23T12:55:05.291390Z", - "iopub.status.idle": "2026-09-23T12:55:05.297772Z", - "shell.execute_reply": "2026-09-23T12:55:05.297772Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "OracleVS setup step ready.\n" - ] - } - ], - "source": [ - "oracle_vs_setup_js = r\"\"\"\n", - "import {\n", - " DistanceStrategy,\n", - " OracleVS,\n", - " VectorElementFormat,\n", - " createIndex,\n", - " dropTablePurge,\n", - "} from \"@oracle/langchain-oracledb\";\n", - "\n", - "import { createVectorIndex, tableName, writeResults } from \"./db_config.mjs\";\n", - "import { checkTablespaceQuota, runTablePreflight, tableExists } from \"./db_preflight.mjs\";\n", - "import { docsForDemo } from \"./demo_corpus.mjs\";\n", - "import { DemoEmbeddings } from \"./demo_embeddings.mjs\";\n", - "\n", - "export async function prepareVectorStore(pool, connection, summary) {\n", - " await runTablePreflight(connection, summary);\n", - "\n", - " if (await tableExists(connection, tableName)) {\n", - " await dropTablePurge(connection, tableName);\n", - " summary.push({ step: \"reset_vector_store\", status: \"PASS\", detail: `Dropped existing ${tableName}` });\n", - " } else {\n", - " summary.push({ step: \"reset_vector_store\", status: \"PASS\", detail: `${tableName} did not exist before this run` });\n", - " }\n", - "\n", - " const quotaCheck = await checkTablespaceQuota(connection);\n", - " summary.push({ step: \"tablespace_quota\", status: quotaCheck.status, detail: quotaCheck.detail });\n", - " if (quotaCheck.status === \"MISSING\") {\n", - " writeResults({\n", - " tableName,\n", - " summary,\n", - " corpus: [],\n", - " searchResults: [],\n", - " filteredResults: [],\n", - " mmrResults: [],\n", - " ragAnswer: \"The database connection works, but this schema does not have enough remaining USERS tablespace quota to create and populate the OracleVS demo table.\",\n", - " integrationSurface: [],\n", - " });\n", - " throw new Error(quotaCheck.detail);\n", - " }\n", - "\n", - " const embeddings = new DemoEmbeddings();\n", - " const documents = docsForDemo();\n", - " const vectorStore = await OracleVS.fromDocuments(documents, embeddings, {\n", - " client: pool,\n", - " tableName,\n", - " query: \"Oracle AI Database LangChain.js RAG\",\n", - " distanceStrategy: DistanceStrategy.COSINE,\n", - " format: VectorElementFormat.FLOAT32,\n", - " description: \"LangChain.js RAG demo table for Oracle AI Database\",\n", - " });\n", - "\n", - " summary.push({ step: \"oraclevs_ingest\", status: \"PASS\", detail: `Inserted ${documents.length} documents into ${tableName}` });\n", - "\n", - " if (createVectorIndex) {\n", - " try {\n", - " const vectorIndexName = `${tableName}_HNSW_IDX`;\n", - " await createIndex(connection, vectorStore, { idxName: vectorIndexName, idxType: \"HNSW\", accuracy: 90, parallel: 1 });\n", - " summary.push({ step: \"vector_index\", status: \"PASS\", detail: `Created or reused ${vectorIndexName}` });\n", - " } catch (error) {\n", - " summary.push({ step: \"vector_index\", status: \"OPTIONAL\", detail: `Vector index skipped by database: ${String(error.message || error).split(\"\\n\")[0].slice(0, 160)}` });\n", - " }\n", - " } else {\n", - " summary.push({ step: \"vector_index\", status: \"READY\", detail: \"Set LANGCHAIN_JS_CREATE_INDEX=true to create an HNSW vector index when the schema has enough quota\" });\n", - " }\n", - "\n", - " return { vectorStore, documents };\n", - "}\n", - "\"\"\"\n", - "\n", - "_ = (RUNTIME_DIR / \"oracle_vs_setup.mjs\").write_text(oracle_vs_setup_js, encoding=\"utf-8\")\n", - "print(\"OracleVS setup step ready.\")" - ] - }, - { - "cell_type": "markdown", - "id": "a1f4b90c", - "metadata": {}, - "source": [ - "## 10. Build the Retrieval Examples\n", - "\n", - "This module shows three LangChain.js retrieval modes over the same Oracle table: similarity search, metadata-filtered search, and maximal marginal relevance." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "ffa0827c", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:05.297772Z", - "iopub.status.busy": "2026-09-23T12:55:05.297772Z", - "iopub.status.idle": "2026-09-23T12:55:05.304264Z", - "shell.execute_reply": "2026-09-23T12:55:05.304264Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieval examples ready.\n" - ] - } - ], - "source": [ - "retrieval_examples_js = r\"\"\"\n", - "import { buildAnswer, docRow } from \"./demo_corpus.mjs\";\n", - "\n", - "export async function runRetrievalExamples(vectorStore, summary) {\n", - " const question = \"How can a JavaScript RAG application use OracleVS with Oracle AI Database as a vector store for retrieval?\";\n", - " const rawResults = await vectorStore.similaritySearchWithScore(question, 4);\n", - " const searchResults = rawResults.map(([doc, score], index) => docRow(doc, score, index + 1));\n", - " summary.push({ step: \"similarity_search\", status: \"PASS\", detail: `Retrieved ${searchResults.length} documents for the main query` });\n", - "\n", - " const filteredRawResults = await vectorStore.similaritySearchWithScore(\n", - " \"How do metadata filters improve retrieval?\",\n", - " 3,\n", - " { category: { \"$eq\": \"retrieval\" } },\n", - " );\n", - " const filteredResults = filteredRawResults.map(([doc, score], index) => docRow(doc, score, index + 1));\n", - " summary.push({ step: \"metadata_filter\", status: \"PASS\", detail: `Retrieved ${filteredResults.length} retrieval-category documents` });\n", - "\n", - " const mmrDocs = await vectorStore.maxMarginalRelevanceSearch(\n", - " \"What does the JavaScript integration provide for RAG?\",\n", - " { k: 3, fetchK: 5, lambda: 0.5 },\n", - " );\n", - " const mmrResults = mmrDocs.map((doc, index) => docRow(doc, undefined, index + 1));\n", - " summary.push({ step: \"mmr_retrieval\", status: \"PASS\", detail: `Selected ${mmrResults.length} diverse documents` });\n", - " summary.push({ step: \"rag_generation\", status: \"PASS\", detail: \"Generated a grounded answer from retrieved OracleVS context\" });\n", - "\n", - " return {\n", - " question,\n", - " searchResults,\n", - " filteredResults,\n", - " mmrResults,\n", - " ragAnswer: await buildAnswer(question, searchResults),\n", - " };\n", - "}\n", - "\"\"\"\n", - "\n", - "_ = (RUNTIME_DIR / \"retrieval_examples.mjs\").write_text(retrieval_examples_js, encoding=\"utf-8\")\n", - "print(\"Retrieval examples ready.\")" - ] - }, - { - "cell_type": "markdown", - "id": "9371fd17", - "metadata": {}, - "source": [ - "## 11. Build the End-to-End Runner\n", - "\n", - "The runner is intentionally short. It opens the database connection, calls the OracleVS setup, runs retrieval, inspects the generated table, and writes one result payload for the notebook to display." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "0aa391fa", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:05.304264Z", - "iopub.status.busy": "2026-09-23T12:55:05.304264Z", - "iopub.status.idle": "2026-09-23T12:55:05.313999Z", - "shell.execute_reply": "2026-09-23T12:55:05.313999Z" - }, - "jupyter": { - "source_hidden": true - }, - "tags": [ - "hide-input" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "End-to-end runner ready.\n" - ] - } - ], - "source": [ - "workflow_js = r\"\"\"\n", - "import { createPool, tableName, writeResults } from \"./db_config.mjs\";\n", - "import { inspectVectorTable } from \"./db_inspection.mjs\";\n", - "import { docsForDemo } from \"./demo_corpus.mjs\";\n", - "import { prepareVectorStore } from \"./oracle_vs_setup.mjs\";\n", - "import { runRetrievalExamples } from \"./retrieval_examples.mjs\";\n", - "\n", - "async function run() {\n", - " const summary = [];\n", - " let pool;\n", - " let connection;\n", - "\n", - " try {\n", - " pool = await createPool();\n", - " connection = await pool.getConnection();\n", - " summary.push({ step: \"connect\", status: \"PASS\", detail: \"Connected with node-oracledb\" });\n", - "\n", - " const { vectorStore, documents } = await prepareVectorStore(pool, connection, summary);\n", - " const retrieval = await runRetrievalExamples(vectorStore, summary);\n", - " const tableInspection = await inspectVectorTable(connection, tableName);\n", - "\n", - " writeResults({\n", - " tableName,\n", - " summary,\n", - " tableInspection,\n", - " corpus: docsForDemo().map((doc, index) => ({\n", - " id: index + 1,\n", - " source: doc.metadata.source,\n", - " category: doc.metadata.category,\n", - " product: doc.metadata.product,\n", - " priority: doc.metadata.priority,\n", - " audience: doc.metadata.audience,\n", - " text: doc.pageContent,\n", - " })),\n", - " ...retrieval,\n", - " integrationSurface: [\n", - " { component: \"OracleVS\", purpose: \"Vector storage and similarity search\", demonstrated: \"yes\" },\n", - " { component: \"createIndex\", purpose: \"Create HNSW or IVF vector indexes when supported by the database environment\", demonstrated: \"opt-in\" },\n", - " { component: \"DistanceStrategy\", purpose: \"Cosine, dot product, Euclidean, and other vector distance strategies\", demonstrated: \"yes\" },\n", - " { component: \"VectorElementFormat\", purpose: \"Dense vector storage format selection\", demonstrated: \"yes\" },\n", - " { component: \"OracleDocLoader\", purpose: \"Load files or database content through Oracle AI Database\", demonstrated: \"optional extension\" },\n", - " { component: \"OracleTextSplitter\", purpose: \"Split text through Oracle text processing\", demonstrated: \"optional extension\" },\n", - " { component: \"OracleEmbeddings\", purpose: \"Generate embeddings through Oracle AI Database\", demonstrated: \"optional extension\" },\n", - " { component: \"OracleSummary\", purpose: \"Summarize text through Oracle AI Database\", demonstrated: \"optional extension\" },\n", - " ],\n", - " });\n", - "\n", - " console.log(`Workflow completed: ${documents.length} documents ingested into ${tableName}; ${retrieval.searchResults.length} similarity results, ${retrieval.filteredResults.length} filtered results, ${retrieval.mmrResults.length} MMR results.`);\n", - " } finally {\n", - " if (connection) await connection.close();\n", - " if (pool) await pool.close(0);\n", - " }\n", - "}\n", - "\n", - "run().catch((error) => {\n", - " console.error(error.message || error);\n", - " process.exitCode = 1;\n", - "});\n", - "\"\"\"\n", - "\n", - "_ = (RUNTIME_DIR / \"oracle_langchain_rag_demo.mjs\").write_text(workflow_js, encoding=\"utf-8\")\n", - "print(\"End-to-end runner ready.\")" - ] - }, - { - "cell_type": "markdown", - "id": "b14f64da", - "metadata": {}, - "source": [ - "## 12. Run the End-to-End Workflow\n", - "\n", - "This cell runs the JavaScript application. A successful run means the notebook connected to Oracle Database, created the `OracleVS` table, inserted documents, and executed the retrieval examples." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "8becbc19", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:05.313999Z", - "iopub.status.busy": "2026-09-23T12:55:05.313999Z", - "iopub.status.idle": "2026-09-23T12:55:18.987378Z", - "shell.execute_reply": "2026-09-23T12:55:18.987378Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Workflow completed: 7 documents ingested into LC_JS_RAG_DEMO; 4 similarity results, 2 filtered results, 3 MMR results.\n" - ] - } - ], - "source": [ - "run_result = subprocess.run(\n", - " [NPM_BIN, \"run\", \"demo\"],\n", - " cwd=RUNTIME_DIR,\n", - " env=RUN_ENV,\n", - " text=True,\n", - " encoding=\"utf-8\",\n", - " errors=\"replace\",\n", - " capture_output=True,\n", - ")\n", - "\n", - "if run_result.stdout.strip():\n", - " clean_stdout = \"\\n\".join(\n", - " line for line in run_result.stdout.splitlines()\n", - " if line.strip() and not line.startswith(\">\") and \"injected env\" not in line\n", - " )\n", - " if clean_stdout:\n", - " print(clean_stdout[-1200:])\n", - "if run_result.stderr.strip() and run_result.returncode != 0:\n", - " print(run_result.stderr[-3000:])\n", - "\n", - "if run_result.returncode != 0:\n", - " results_path = RUNTIME_DIR / \"rag_results.json\"\n", - " if results_path.exists():\n", - " diagnostic_payload = json.loads(results_path.read_text(encoding=\"utf-8\"))\n", - " quota_missing = any(\n", - " row.get(\"step\") == \"tablespace_quota\" and row.get(\"status\") == \"MISSING\"\n", - " for row in diagnostic_payload.get(\"summary\", [])\n", - " )\n", - " if quota_missing:\n", - " print(\"Preflight stopped before OracleVS because this schema does not have enough remaining quota.\")\n", - " else:\n", - " raise RuntimeError(\"The LangChain.js Oracle RAG demo failed. Review the JavaScript error above.\")\n", - " else:\n", - " raise RuntimeError(\"The LangChain.js Oracle RAG demo failed. Review the JavaScript error above.\")" - ] - }, - { - "cell_type": "markdown", - "id": "bafdf6e0", - "metadata": {}, - "source": [ - "## 13. Inspect the Oracle Vector Store" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "85871fb2", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:18.989679Z", - "iopub.status.busy": "2026-09-23T12:55:18.989679Z", - "iopub.status.idle": "2026-09-23T12:55:19.431221Z", - "shell.execute_reply": "2026-09-23T12:55:19.431221Z" - } - }, - "outputs": [ - { - "data": { - "text/markdown": [ - "**Vector table:** `LC_JS_RAG_DEMO`\n", - "\n", - "The status table shows each database and LangChain.js capability checked by the notebook." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
stepstatusdetail
connectPASSConnected with node-oracledb
table_operationsPASSCreated, inserted, selected 1 row, and dropped a test table
reset_vector_storePASSDropped existing LC_JS_RAG_DEMO
tablespace_quotaPASSUSERS: 2.00 MB used of 10.00 MB; 8.00 MB remains
oraclevs_ingestPASSInserted 7 documents into LC_JS_RAG_DEMO
vector_indexREADYSet LANGCHAIN_JS_CREATE_INDEX=true to create an HNSW vector index when the schema has enough quota
similarity_searchPASSRetrieved 4 documents for the main query
metadata_filterPASSRetrieved 2 retrieval-category documents
mmr_retrievalPASSSelected 3 diverse documents
rag_generationPASSGenerated a grounded answer from retrieved OracleVS context
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import json\n", - "from pathlib import Path\n", - "\n", - "import pandas as pd\n", - "try:\n", - " from IPython.display import HTML, Markdown, display\n", - "except ImportError:\n", - " class HTML(str):\n", - " pass\n", - "\n", - " class Markdown(str):\n", - " pass\n", - "\n", - " def display(value):\n", - " print(value)\n", - "\n", - "\n", - "def show_table(df, caption=None):\n", - " # Display compact tutorial tables without the pandas row index.\n", - " if caption:\n", - " display(Markdown(caption))\n", - " display(HTML(df.to_html(index=False, escape=False)))\n", - "\n", - "results_path = RUNTIME_DIR / \"rag_results.json\"\n", - "payload = json.loads(results_path.read_text(encoding=\"utf-8\"))\n", - "\n", - "display(Markdown(\n", - " f\"**Vector table:** `{payload['tableName']}`\\n\\n\"\n", - " \"The status table shows each database and LangChain.js capability checked by the notebook.\"\n", - "))\n", - "show_table(pd.DataFrame(payload[\"summary\"]))\n", - "preflight_stopped = not payload.get(\"corpus\")" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "0a11e6c3", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:19.431221Z", - "iopub.status.busy": "2026-09-23T12:55:19.431221Z", - "iopub.status.idle": "2026-09-23T12:55:19.440525Z", - "shell.execute_reply": "2026-09-23T12:55:19.440092Z" - } - }, - "outputs": [ - { - "data": { - "text/markdown": [ - "**What OracleVS created**\n", - "\n", - "`OracleVS` created one Oracle table, stored `7` LangChain documents, and used about `0.063 MB` of table segment space." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/markdown": [ - "The generated table keeps document text, JSON metadata, and the native Oracle `VECTOR` column together." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
columntype
IDRAW
EXTERNAL_IDVARCHAR2
EMBEDDINGVECTOR
TEXTCLOB
METADATAJSON
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/markdown": [ - "No user-created vector index is shown because `LANGCHAIN_JS_CREATE_INDEX` is `false` by default for FreeSQL-friendly runs." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "if preflight_stopped:\n", - " display(Markdown(\"The database connection works, but the vector table was not created because the preflight stopped first.\"))\n", - "else:\n", - " inspection = payload[\"tableInspection\"]\n", - " display(Markdown(\n", - " f\"**What OracleVS created**\\n\\n\"\n", - " f\"`OracleVS` created one Oracle table, stored `{inspection['rowCount']}` LangChain documents, \"\n", - " f\"and used about `{inspection['storageMb']} MB` of table segment space.\"\n", - " ))\n", - " show_table(\n", - " pd.DataFrame(inspection[\"columns\"]),\n", - " \"The generated table keeps document text, JSON metadata, and the native Oracle `VECTOR` column together.\"\n", - " )\n", - " user_indexes = [\n", - " row for row in inspection[\"indexes\"]\n", - " if not str(row[\"index\"]).startswith(\"SYS_\")\n", - " ]\n", - " if user_indexes:\n", - " display(Markdown(\"**User-created vector indexes**\"))\n", - " show_table(pd.DataFrame(user_indexes))\n", - " else:\n", - " display(Markdown(\"No user-created vector index is shown because `LANGCHAIN_JS_CREATE_INDEX` is `false` by default for FreeSQL-friendly runs.\"))" - ] - }, - { - "cell_type": "markdown", - "id": "c9227551", - "metadata": {}, - "source": [ - "## 14. Review the Demo Corpus" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "7803d710", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:19.443075Z", - "iopub.status.busy": "2026-09-23T12:55:19.442405Z", - "iopub.status.idle": "2026-09-23T12:55:19.449332Z", - "shell.execute_reply": "2026-09-23T12:55:19.449332Z" - } - }, - "outputs": [ - { - "data": { - "text/markdown": [ - "This small corpus is intentionally structured with metadata so the retrieval section can show both semantic search and metadata filtering." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
idcategoryproductpriorityaudiencetext
1databaseOracle AI DatabasehigharchitectOracle AI Database stores vectors alongside operational data, so RAG applications can keep retrieval close to governed enterprise records.
2frameworkLangChain.jshighdeveloperLangChain.js RAG applications use OracleVS with Oracle AI Database as a vector store for adding documents, preserving metadata, and running similarity search retrieval.
3setupFreeSQLmediumdeveloperFreeSQL gives developers a hosted Oracle Database schema for tutorials that only need normal schema table operations.
4retrievalOracleVShighdeveloperMetadata filters narrow OracleVS retrieval by product, source, category, tenant, priority, or other JSON metadata fields.
5retrievalLangChain.jsmediumdeveloperMaximal marginal relevance helps LangChain.js retrieve a diverse context set instead of returning several near-duplicate chunks.
6architectureLangChain.jsmediumarchitectA production RAG service can swap the demo embedding class for OpenAI, OCI Generative AI, or Oracle-backed embeddings without changing OracleVS retrieval code.
7setupnode-oracledbmediumdeveloperAutonomous AI Database and local Oracle Database use the same node-oracledb connection pattern when the schema can create and manage application tables.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "corpus_df = pd.DataFrame(payload[\"corpus\"])\n", - "if preflight_stopped:\n", - " display(Markdown(\"Document ingestion did not run because the database preflight stopped first.\"))\n", - "else:\n", - " show_table(\n", - " corpus_df.reindex(columns=[\"id\", \"category\", \"product\", \"priority\", \"audience\", \"text\"]),\n", - " \"This small corpus is intentionally structured with metadata so the retrieval section can show both semantic search and metadata filtering.\"\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "f07acccc", - "metadata": {}, - "source": [ - "## 15. Compare Retrieval Modes" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "3ad0ab48", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:19.449332Z", - "iopub.status.busy": "2026-09-23T12:55:19.449332Z", - "iopub.status.idle": "2026-09-23T12:55:19.463749Z", - "shell.execute_reply": "2026-09-23T12:55:19.463749Z" - } - }, - "outputs": [ - { - "data": { - "text/markdown": [ - "**Similarity search** retrieves the nearest documents by vector distance. Lower cosine distance is closer." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
rankdistancesourcecategoryproductsnippet
10.123145oracle-ai-databasedatabaseOracle AI DatabaseOracle AI Database stores vectors alongside operational data, so RAG applications can keep retrieval close to governed enterprise records.
20.148912embedding-providerarchitectureLangChain.jsA production RAG service can swap the demo embedding class for OpenAI, OCI Generative AI, or Oracle-backed embeddings without changing OracleVS retrieval code.
30.153764langchain-jsframeworkLangChain.jsLangChain.js RAG applications use OracleVS with Oracle AI Database as a vector store for adding documents, preserving metadata, and running similarity search retrieval.
40.172201connection-optionssetupnode-oracledbAutonomous AI Database and local Oracle Database use the same node-oracledb connection pattern when the schema can create and manage application tables.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/markdown": [ - "**Metadata-filtered search** applies a JSON metadata predicate before returning context. Here the filter is `category = retrieval`." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
rankdistancesourcecategoryproductsnippet
10.092271metadata-filteringretrievalOracleVSMetadata filters narrow OracleVS retrieval by product, source, category, tenant, priority, or other JSON metadata fields.
20.729846mmrretrievalLangChain.jsMaximal marginal relevance helps LangChain.js retrieve a diverse context set instead of returning several near-duplicate chunks.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/markdown": [ - "**MMR retrieval** balances relevance and diversity, so the context set does not collapse into near-duplicate chunks." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
ranksourcecategoryproductsnippet
1oracle-ai-databasedatabaseOracle AI DatabaseOracle AI Database stores vectors alongside operational data, so RAG applications can keep retrieval close to governed enterprise records.
2mmrretrievalLangChain.jsMaximal marginal relevance helps LangChain.js retrieve a diverse context set instead of returning several near-duplicate chunks.
3embedding-providerarchitectureLangChain.jsA production RAG service can swap the demo embedding class for OpenAI, OCI Generative AI, or Oracle-backed embeddings without changing OracleVS retrieval code.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "search_df = pd.DataFrame(payload[\"searchResults\"])\n", - "filtered_df = pd.DataFrame(payload[\"filteredResults\"])\n", - "mmr_df = pd.DataFrame(payload[\"mmrResults\"])\n", - "\n", - "if preflight_stopped:\n", - " display(Markdown(\"Retrieval examples are skipped because document ingestion did not run.\"))\n", - "else:\n", - " show_table(\n", - " search_df.reindex(columns=[\"rank\", \"distance\", \"source\", \"category\", \"product\", \"snippet\"]),\n", - " \"**Similarity search** retrieves the nearest documents by vector distance. Lower cosine distance is closer.\"\n", - " )\n", - " show_table(\n", - " filtered_df.reindex(columns=[\"rank\", \"distance\", \"source\", \"category\", \"product\", \"snippet\"]),\n", - " \"**Metadata-filtered search** applies a JSON metadata predicate before returning context. Here the filter is `category = retrieval`.\"\n", - " )\n", - " show_table(\n", - " mmr_df.reindex(columns=[\"rank\", \"source\", \"category\", \"product\", \"snippet\"]),\n", - " \"**MMR retrieval** balances relevance and diversity, so the context set does not collapse into near-duplicate chunks.\"\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "1bece7ce", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:19.463749Z", - "iopub.status.busy": "2026-09-23T12:55:19.463749Z", - "iopub.status.idle": "2026-09-23T12:55:19.473636Z", - "shell.execute_reply": "2026-09-23T12:55:19.473636Z" - } - }, - "outputs": [ - { - "data": { - "text/markdown": [ - "This comparison shows how the same vector store supports different retrieval modes through LangChain.js." - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
moderanksourcecategorydistance
similarity1oracle-ai-databasedatabase0.123145
similarity2embedding-providerarchitecture0.148912
similarity3langchain-jsframework0.153764
similarity4connection-optionssetup0.172201
metadata filter1metadata-filteringretrieval0.092271
metadata filter2mmrretrieval0.729846
MMR diversity1oracle-ai-databasedatabasenot returned
MMR diversity2mmrretrievalnot returned
MMR diversity3embedding-providerarchitecturenot returned
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "if preflight_stopped:\n", - " display(Markdown(\"Retrieval comparison is skipped because document ingestion did not run.\"))\n", - "else:\n", - " comparison_rows = []\n", - " for row in search_df.to_dict(\"records\"):\n", - " comparison_rows.append({\n", - " \"mode\": \"similarity\",\n", - " \"rank\": row[\"rank\"],\n", - " \"source\": row[\"source\"],\n", - " \"category\": row[\"category\"],\n", - " \"distance\": row.get(\"distance\", row.get(\"score\")),\n", - " })\n", - " for row in filtered_df.to_dict(\"records\"):\n", - " comparison_rows.append({\n", - " \"mode\": \"metadata filter\",\n", - " \"rank\": row[\"rank\"],\n", - " \"source\": row[\"source\"],\n", - " \"category\": row[\"category\"],\n", - " \"distance\": row.get(\"distance\", row.get(\"score\")),\n", - " })\n", - " for row in mmr_df.to_dict(\"records\"):\n", - " comparison_rows.append({\n", - " \"mode\": \"MMR diversity\",\n", - " \"rank\": row[\"rank\"],\n", - " \"source\": row[\"source\"],\n", - " \"category\": row[\"category\"],\n", - " \"distance\": \"not returned\",\n", - " })\n", - " show_table(\n", - " pd.DataFrame(comparison_rows),\n", - " \"This comparison shows how the same vector store supports different retrieval modes through LangChain.js.\"\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "fe40ff94", - "metadata": {}, - "source": [ - "## 16. Generate a Grounded Answer" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "caf27fad", - "metadata": { - "execution": { - "iopub.execute_input": "2026-09-23T12:55:19.473636Z", - "iopub.status.busy": "2026-09-23T12:55:19.473636Z", - "iopub.status.idle": "2026-09-23T12:55:19.479428Z", - "shell.execute_reply": "2026-09-23T12:55:19.479428Z" - } - }, - "outputs": [ - { - "data": { - "text/markdown": [ - "The final answer below is generated by the configured chat model from the retrieved rows above. The prompt instructs the model to answer only from retrieved context.\n", - "\n", - "```text\n", - "Question: How can a JavaScript RAG application use OracleVS with Oracle AI Database as a vector store for retrieval?\n", - "\n", - "Grounded answer generated by gpt-4o-mini:\n", - "A JavaScript RAG application can utilize OracleVS with Oracle AI Database as a vector store by leveraging the capabilities of LangChain.js. This framework allows the application to add documents, preserve metadata, and perform similarity search retrieval using the Oracle AI Database, which stores vectors alongside operational data. Additionally, the application can easily swap embedding classes, such as OpenAI or OCI Generative AI, without altering the retrieval code for OracleVS, ensuring flexibility in embedding options. This integration keeps retrieval processes close to governed enterprise records, enhancing data management and accessibility.\n", - "\n", - "Retrieved context used:\n", - "[1] source=oracle-ai-database; product=Oracle AI Database; category=database; snippet=Oracle AI Database stores vectors alongside operational data, so RAG applications can keep retrieval close to governed enterprise records.\n", - "[2] source=embedding-provider; product=LangChain.js; category=architecture; snippet=A production RAG service can swap the demo embedding class for OpenAI, OCI Generative AI, or Oracle-backed embeddings without changing OracleVS retrieval code.\n", - "[3] source=langchain-js; product=LangChain.js; category=framework; snippet=LangChain.js RAG applications use OracleVS with Oracle AI Database as a vector store for adding documents, preserving metadata, and running similarity search retrieval.\n", - "[4] source=connection-options; product=node-oracledb; category=setup; snippet=Autonomous AI Database and local Oracle Database use the same node-oracledb connection pattern when the schema can create and manage application tables.\n", - "```" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "display(Markdown(\n", - " \"The final answer below is generated by the configured chat model from the retrieved rows above. \"\n", - " \"The prompt instructs the model to answer only from retrieved context.\\n\\n\"\n", - " \"```text\\n\" + payload[\"ragAnswer\"] + \"\\n```\"\n", - "))" - ] - }, - { - "cell_type": "markdown", - "id": "7d5d0ef5", - "metadata": {}, - "source": [ - "## TypeScript Notes\n", - "\n", - "The notebook runs JavaScript because Node.js can execute `.mjs` files directly. In a TypeScript application, use the same package and imports, then compile with your normal TypeScript build.\n", - "\n", - "```typescript\n", - "import {\n", - " OracleVS,\n", - " DistanceStrategy,\n", - " VectorElementFormat,\n", - " createIndex,\n", - "} from \"@oracle/langchain-oracledb\";\n", - "```\n", - "\n", - "The integration also exposes optional Oracle AI Database utilities for document loading, text splitting, embeddings, and summarization:\n", - "\n", - "```typescript\n", - "import {\n", - " OracleDocLoader,\n", - " OracleTextSplitter,\n", - " OracleEmbeddings,\n", - " OracleSummary,\n", - "} from \"@oracle/langchain-oracledb\";\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "7641179b", - "metadata": {}, - "source": [ - "## Integration Surface\n", - "\n", - "This tutorial focuses on the most direct LangChain.js RAG path: `OracleVS` as the vector store, Node.js as the application runtime, and Oracle AI Database as the storage and retrieval layer.\n", - "\n", - "| Component | Purpose | Covered in this tutorial |\n", - "| --- | --- | --- |\n", - "| `OracleVS` | Store and query document vectors in Oracle AI Database | Runnable end-to-end |\n", - "| `DistanceStrategy` | Choose vector distance behavior such as cosine distance | Runnable end-to-end |\n", - "| `VectorElementFormat` | Control dense vector storage format | Runnable end-to-end |\n", - "| Metadata filters | Restrict retrieval through JSON metadata predicates | Runnable end-to-end |\n", - "| MMR retrieval | Return a diverse context set instead of near-duplicate chunks | Runnable end-to-end |\n", - "| `createIndex` | Create HNSW or IVF vector indexes when the database environment supports it | Opt-in runtime path |\n", - "| `OracleDocLoader` | Load supported documents through Oracle AI Database | Extension path |\n", - "| `OracleTextSplitter` | Split text through Oracle text processing | Extension path |\n", - "| `OracleEmbeddings` | Generate embeddings through Oracle AI Database | Extension path |\n", - "| `OracleSummary` | Summarize text through Oracle AI Database | Extension path |\n", - "\n", - "The runnable path is intentionally small enough to work as a first tutorial. The extension path is where a production application can add database-backed document loading, Oracle text splitting, in-database embedding generation, summarization, and vector-index tuning.\n" - ] - }, - { - "cell_type": "markdown", - "id": "dae30c46", - "metadata": {}, - "source": [ - "## Learn More\n", - "\n", - "- [Oracle LangChain JavaScript integration guide](https://docs.oracle.com/en/database/oracle/oracle-database/26/aintg/langchain-oracledb-integration-guide/langchain-javascript.html)\n", - "- [Oracle AI Database integrations overview](https://docs.oracle.com/en/database/oracle/oracle-database/26/aintg/integrations.html)\n", - "- [LangChain.js Oracle AI Database vector store docs](https://docs.langchain.com/oss/javascript/integrations/vectorstores/oracleai)\n", - "- [npm: @oracle/langchain-oracledb](https://www.npmjs.com/package/@oracle/langchain-oracledb)\n", - "- [Source: oracle/langchain-oracle](https://github.com/oracle/langchain-oracle/tree/main/libs/js/langchain-oracledb)" - ] - }, - { - "cell_type": "markdown", - "id": "2e2acf14", - "metadata": {}, - "source": [ - "## What This Proves\n", - "\n", - "This notebook validates the first-party tutorial path for the LangChain TypeScript/JavaScript integration:\n", - "\n", - "- `@oracle/langchain-oracledb` can create and populate an Oracle AI Database vector table from a JavaScript application.\n", - "- `OracleVS` supports document storage, semantic retrieval, metadata filtering, vector-index setup, and MMR retrieval.\n", - "- FreeSQL can be used as the hosted Oracle Database target when the schema can create normal application tables and has enough quota.\n", - "- The retrieved OracleVS context is passed to a chat model to generate the final grounded RAG answer.\n", - "- The same pattern can move to TypeScript, production embeddings, Autonomous AI Database, or local Oracle Database without changing the core LangChain RAG flow.\n", - "\n", - "For continuous improvement, the next useful tutorials would be deeper ingestion examples with `OracleDocLoader` and `OracleTextSplitter`, plus production embedding and summarization examples with `OracleEmbeddings` and `OracleSummary`.\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv (3.11.6)", - "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.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}