Просмотр исходного кода

feat: support oracle oci autonomouse database. Fixes #14792 and Fixes #14628. (#14804)

Co-authored-by: engchina <atjapan2015@gmail.com>
engchina 1 месяц назад
Родитель
Сommit
c8de30f3d9

+ 23 - 12
api/configs/middleware/vdb/oracle_config.py

@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import Field, PositiveInt
+from pydantic import Field
 from pydantic_settings import BaseSettings
 
 
@@ -9,27 +9,38 @@ class OracleConfig(BaseSettings):
     Configuration settings for Oracle database
     """
 
-    ORACLE_HOST: Optional[str] = Field(
-        description="Hostname or IP address of the Oracle database server (e.g., 'localhost' or 'oracle.example.com')",
+    ORACLE_USER: Optional[str] = Field(
+        description="Username for authenticating with the Oracle database",
         default=None,
     )
 
-    ORACLE_PORT: PositiveInt = Field(
-        description="Port number on which the Oracle database server is listening (default is 1521)",
-        default=1521,
+    ORACLE_PASSWORD: Optional[str] = Field(
+        description="Password for authenticating with the Oracle database",
+        default=None,
     )
 
-    ORACLE_USER: Optional[str] = Field(
-        description="Username for authenticating with the Oracle database",
+    ORACLE_DSN: Optional[str] = Field(
+        description="Oracle database connection string. For traditional database, use format 'host:port/service_name'. "
+        "For autonomous database, use the service name from tnsnames.ora in the wallet",
         default=None,
     )
 
-    ORACLE_PASSWORD: Optional[str] = Field(
-        description="Password for authenticating with the Oracle database",
+    ORACLE_CONFIG_DIR: Optional[str] = Field(
+        description="Directory containing the tnsnames.ora configuration file. Only used in thin mode connection",
         default=None,
     )
 
-    ORACLE_DATABASE: Optional[str] = Field(
-        description="Name of the Oracle database or service to connect to (e.g., 'ORCL' or 'pdborcl')",
+    ORACLE_WALLET_LOCATION: Optional[str] = Field(
+        description="Oracle wallet directory path containing the wallet files for secure connection",
         default=None,
     )
+
+    ORACLE_WALLET_PASSWORD: Optional[str] = Field(
+        description="Password to decrypt the Oracle wallet, if it is encrypted",
+        default=None,
+    )
+
+    ORACLE_IS_AUTONOMOUS: bool = Field(
+        description="Flag indicating whether connecting to Oracle Autonomous Database",
+        default=False,
+    )

+ 39 - 21
api/core/rag/datasource/vdb/oracle/oraclevector.py

@@ -23,25 +23,30 @@ oracledb.defaults.fetch_lobs = False
 
 
 class OracleVectorConfig(BaseModel):
-    host: str
-    port: int
     user: str
     password: str
-    database: str
+    dsn: str
+    config_dir: str | None = None
+    wallet_location: str | None = None
+    wallet_password: str | None = None
+    is_autonomous: bool = False
 
     @model_validator(mode="before")
     @classmethod
     def validate_config(cls, values: dict) -> dict:
-        if not values["host"]:
-            raise ValueError("config ORACLE_HOST is required")
-        if not values["port"]:
-            raise ValueError("config ORACLE_PORT is required")
         if not values["user"]:
             raise ValueError("config ORACLE_USER is required")
         if not values["password"]:
             raise ValueError("config ORACLE_PASSWORD is required")
-        if not values["database"]:
-            raise ValueError("config ORACLE_DB is required")
+        if not values["dsn"]:
+            raise ValueError("config ORACLE_DSN is required")
+        if values.get("is_autonomous", False):
+            if not values.get("config_dir"):
+                raise ValueError("config_dir is required for autonomous database")
+            if not values.get("wallet_location"):
+                raise ValueError("wallet_location is required for autonomous database")
+            if not values.get("wallet_password"):
+                raise ValueError("wallet_password is required for autonomous database")
         return values
 
 
@@ -56,7 +61,7 @@ CREATE TABLE IF NOT EXISTS {table_name} (
 SQL_CREATE_INDEX = """
 CREATE INDEX IF NOT EXISTS idx_docs_{table_name} ON {table_name}(text) 
 INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS 
-('FILTER CTXSYS.NULL_FILTER SECTION GROUP CTXSYS.HTML_SECTION_GROUP LEXER sys.my_chinese_vgram_lexer')
+('FILTER CTXSYS.NULL_FILTER SECTION GROUP CTXSYS.HTML_SECTION_GROUP LEXER multilingual_lexer')
 """
 
 
@@ -103,14 +108,25 @@ class OracleVector(BaseVector):
             )
 
     def _create_connection_pool(self, config: OracleVectorConfig):
-        return oracledb.create_pool(
-            user=config.user,
-            password=config.password,
-            dsn="{}:{}/{}".format(config.host, config.port, config.database),
-            min=1,
-            max=50,
-            increment=1,
-        )
+        pool_params = {
+            "user": config.user,
+            "password": config.password,
+            "dsn": config.dsn,
+            "min": 1,
+            "max": 50,
+            "increment": 1,
+        }
+
+        if config.is_autonomous:
+            pool_params.update(
+                {
+                    "config_dir": config.config_dir,
+                    "wallet_location": config.wallet_location,
+                    "wallet_password": config.wallet_password,
+                }
+            )
+
+        return oracledb.create_pool(**pool_params)
 
     @contextmanager
     def _get_cursor(self):
@@ -287,10 +303,12 @@ class OracleVectorFactory(AbstractVectorFactory):
         return OracleVector(
             collection_name=collection_name,
             config=OracleVectorConfig(
-                host=dify_config.ORACLE_HOST or "localhost",
-                port=dify_config.ORACLE_PORT,
                 user=dify_config.ORACLE_USER or "system",
                 password=dify_config.ORACLE_PASSWORD or "oracle",
-                database=dify_config.ORACLE_DATABASE or "orcl",
+                dsn=dify_config.ORACLE_DSN or "oracle:1521/freepdb1",
+                config_dir=dify_config.ORACLE_CONFIG_DIR,
+                wallet_location=dify_config.ORACLE_WALLET_LOCATION,
+                wallet_password=dify_config.ORACLE_WALLET_PASSWORD,
+                is_autonomous=dify_config.ORACLE_IS_AUTONOMOUS,
             ),
         )

+ 1 - 3
api/tests/integration_tests/vdb/oracle/test_oraclevector.py

@@ -13,11 +13,9 @@ class OracleVectorTest(AbstractVectorTest):
         self.vector = OracleVector(
             collection_name=self.collection_name,
             config=OracleVectorConfig(
-                host="localhost",
-                port=1521,
                 user="dify",
                 password="dify",
-                database="FREEPDB1",
+                dsn="localhost:1521/FREEPDB1",
             ),
         )
 

+ 5 - 3
docker/.env.example

@@ -483,11 +483,13 @@ CHROMA_AUTH_PROVIDER=chromadb.auth.token_authn.TokenAuthClientProvider
 CHROMA_AUTH_CREDENTIALS=
 
 # Oracle configuration, only available when VECTOR_STORE is `oracle`
-ORACLE_HOST=oracle
-ORACLE_PORT=1521
 ORACLE_USER=dify
 ORACLE_PASSWORD=dify
-ORACLE_DATABASE=FREEPDB1
+ORACLE_DSN=oracle:1521/FREEPDB1
+ORACLE_CONFIG_DIR=/app/api/storage/wallet
+ORACLE_WALLET_LOCATION=/app/api/storage/wallet
+ORACLE_WALLET_PASSWORD=dify
+ORACLE_IS_AUTONOMOUS=false
 
 # relyt configurations, only available when VECTOR_STORE is `relyt`
 RELYT_HOST=db

+ 5 - 3
docker/docker-compose.yaml

@@ -197,11 +197,13 @@ x-shared-env: &shared-api-worker-env
   CHROMA_DATABASE: ${CHROMA_DATABASE:-default_database}
   CHROMA_AUTH_PROVIDER: ${CHROMA_AUTH_PROVIDER:-chromadb.auth.token_authn.TokenAuthClientProvider}
   CHROMA_AUTH_CREDENTIALS: ${CHROMA_AUTH_CREDENTIALS:-}
-  ORACLE_HOST: ${ORACLE_HOST:-oracle}
-  ORACLE_PORT: ${ORACLE_PORT:-1521}
   ORACLE_USER: ${ORACLE_USER:-dify}
   ORACLE_PASSWORD: ${ORACLE_PASSWORD:-dify}
-  ORACLE_DATABASE: ${ORACLE_DATABASE:-FREEPDB1}
+  ORACLE_DSN: ${ORACLE_DSN:-oracle:1521/FREEPDB1}
+  ORACLE_CONFIG_DIR: ${ORACLE_CONFIG_DIR:-/app/api/storage/wallet}
+  ORACLE_WALLET_LOCATION: ${ORACLE_WALLET_LOCATION:-/app/api/storage/wallet}
+  ORACLE_WALLET_PASSWORD: ${ORACLE_WALLET_PASSWORD:-dify}
+  ORACLE_IS_AUTONOMOUS: ${ORACLE_IS_AUTONOMOUS:-false}
   RELYT_HOST: ${RELYT_HOST:-db}
   RELYT_PORT: ${RELYT_PORT:-5432}
   RELYT_USER: ${RELYT_USER:-postgres}

+ 1 - 1
docker/startupscripts/init_user.script

@@ -5,6 +5,6 @@ create user dify identified by dify DEFAULT TABLESPACE users quota unlimited on
 grant DB_DEVELOPER_ROLE to dify;
 
 BEGIN
-CTX_DDL.CREATE_PREFERENCE('my_chinese_vgram_lexer','CHINESE_VGRAM_LEXER');
+CTX_DDL.CREATE_PREFERENCE('dify.multilingual_lexer','CHINESE_VGRAM_LEXER');
 END;
 /