Merge branch 'langgenius:main' into fix-doc-TOC-style

pull/18314/head
GuanMu 1 year ago committed by GitHub
commit 00606afb54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,8 +1,8 @@
from collections.abc import Sequence from collections.abc import Sequence
from enum import Enum, StrEnum from enum import Enum, StrEnum
from typing import Optional from typing import Any, Optional, Union
from pydantic import BaseModel, Field, field_validator from pydantic import BaseModel, Field, field_serializer, field_validator
class PromptMessageRole(Enum): class PromptMessageRole(Enum):
@ -135,6 +135,16 @@ class PromptMessage(BaseModel):
""" """
return not self.content return not self.content
@field_serializer("content")
def serialize_content(
self, content: Optional[Union[str, Sequence[PromptMessageContent]]]
) -> Optional[str | list[dict[str, Any] | PromptMessageContent] | Sequence[PromptMessageContent]]:
if content is None or isinstance(content, str):
return content
if isinstance(content, list):
return [item.model_dump() if hasattr(item, "model_dump") else item for item in content]
return content
class UserPromptMessage(PromptMessage): class UserPromptMessage(PromptMessage):
""" """

@ -2,12 +2,12 @@ import array
import json import json
import re import re
import uuid import uuid
from contextlib import contextmanager
from typing import Any from typing import Any
import jieba.posseg as pseg # type: ignore import jieba.posseg as pseg # type: ignore
import numpy import numpy
import oracledb import oracledb
from oracledb.connection import Connection
from pydantic import BaseModel, model_validator from pydantic import BaseModel, model_validator
from configs import dify_config from configs import dify_config
@ -70,6 +70,7 @@ class OracleVector(BaseVector):
super().__init__(collection_name) super().__init__(collection_name)
self.pool = self._create_connection_pool(config) self.pool = self._create_connection_pool(config)
self.table_name = f"embedding_{collection_name}" self.table_name = f"embedding_{collection_name}"
self.config = config
def get_type(self) -> str: def get_type(self) -> str:
return VectorType.ORACLE return VectorType.ORACLE
@ -107,16 +108,19 @@ class OracleVector(BaseVector):
outconverter=self.numpy_converter_out, outconverter=self.numpy_converter_out,
) )
def _get_connection(self) -> Connection:
connection = oracledb.connect(user=self.config.user, password=self.config.password, dsn=self.config.dsn)
return connection
def _create_connection_pool(self, config: OracleVectorConfig): def _create_connection_pool(self, config: OracleVectorConfig):
pool_params = { pool_params = {
"user": config.user, "user": config.user,
"password": config.password, "password": config.password,
"dsn": config.dsn, "dsn": config.dsn,
"min": 1, "min": 1,
"max": 50, "max": 5,
"increment": 1, "increment": 1,
} }
if config.is_autonomous: if config.is_autonomous:
pool_params.update( pool_params.update(
{ {
@ -125,22 +129,8 @@ class OracleVector(BaseVector):
"wallet_password": config.wallet_password, "wallet_password": config.wallet_password,
} }
) )
return oracledb.create_pool(**pool_params) return oracledb.create_pool(**pool_params)
@contextmanager
def _get_cursor(self):
conn = self.pool.acquire()
conn.inputtypehandler = self.input_type_handler
conn.outputtypehandler = self.output_type_handler
cur = conn.cursor()
try:
yield cur
finally:
cur.close()
conn.commit()
conn.close()
def create(self, texts: list[Document], embeddings: list[list[float]], **kwargs): def create(self, texts: list[Document], embeddings: list[list[float]], **kwargs):
dimension = len(embeddings[0]) dimension = len(embeddings[0])
self._create_collection(dimension) self._create_collection(dimension)
@ -162,41 +152,68 @@ class OracleVector(BaseVector):
numpy.array(embeddings[i]), numpy.array(embeddings[i]),
) )
) )
# print(f"INSERT INTO {self.table_name} (id, text, meta, embedding) VALUES (:1, :2, :3, :4)") with self._get_connection() as conn:
with self._get_cursor() as cur: conn.inputtypehandler = self.input_type_handler
cur.executemany( conn.outputtypehandler = self.output_type_handler
f"INSERT INTO {self.table_name} (id, text, meta, embedding) VALUES (:1, :2, :3, :4)", values # with conn.cursor() as cur:
) # cur.executemany(
# f"INSERT INTO {self.table_name} (id, text, meta, embedding) VALUES (:1, :2, :3, :4)", values
# )
# conn.commit()
for value in values:
with conn.cursor() as cur:
try:
cur.execute(
f"""INSERT INTO {self.table_name} (id, text, meta, embedding)
VALUES (:1, :2, :3, :4)""",
value,
)
conn.commit()
except Exception as e:
print(e)
conn.close()
return pks return pks
def text_exists(self, id: str) -> bool: def text_exists(self, id: str) -> bool:
with self._get_cursor() as cur: with self._get_connection() as conn:
cur.execute(f"SELECT id FROM {self.table_name} WHERE id = '%s'" % (id,)) with conn.cursor() as cur:
return cur.fetchone() is not None cur.execute(f"SELECT id FROM {self.table_name} WHERE id = '%s'" % (id,))
return cur.fetchone() is not None
conn.close()
def get_by_ids(self, ids: list[str]) -> list[Document]: def get_by_ids(self, ids: list[str]) -> list[Document]:
with self._get_cursor() as cur: with self._get_connection() as conn:
cur.execute(f"SELECT meta, text FROM {self.table_name} WHERE id IN %s", (tuple(ids),)) with conn.cursor() as cur:
docs = [] cur.execute(f"SELECT meta, text FROM {self.table_name} WHERE id IN %s", (tuple(ids),))
for record in cur: docs = []
docs.append(Document(page_content=record[1], metadata=record[0])) for record in cur:
docs.append(Document(page_content=record[1], metadata=record[0]))
self.pool.release(connection=conn)
conn.close()
return docs return docs
def delete_by_ids(self, ids: list[str]) -> None: def delete_by_ids(self, ids: list[str]) -> None:
if not ids: if not ids:
return return
with self._get_cursor() as cur: with self._get_connection() as conn:
cur.execute(f"DELETE FROM {self.table_name} WHERE id IN %s" % (tuple(ids),)) with conn.cursor() as cur:
cur.execute(f"DELETE FROM {self.table_name} WHERE id IN %s" % (tuple(ids),))
conn.commit()
conn.close()
def delete_by_metadata_field(self, key: str, value: str) -> None: def delete_by_metadata_field(self, key: str, value: str) -> None:
with self._get_cursor() as cur: with self._get_connection() as conn:
cur.execute(f"DELETE FROM {self.table_name} WHERE meta->>%s = %s", (key, value)) with conn.cursor() as cur:
cur.execute(f"DELETE FROM {self.table_name} WHERE meta->>%s = %s", (key, value))
conn.commit()
conn.close()
def search_by_vector(self, query_vector: list[float], **kwargs: Any) -> list[Document]: def search_by_vector(self, query_vector: list[float], **kwargs: Any) -> list[Document]:
""" """
Search the nearest neighbors to a vector. Search the nearest neighbors to a vector.
:param query_vector: The input vector to search for similar items. :param query_vector: The input vector to search for similar items.
:param top_k: The number of nearest neighbors to return, default is 5.
:return: List of Documents that are nearest to the query vector. :return: List of Documents that are nearest to the query vector.
""" """
top_k = kwargs.get("top_k", 4) top_k = kwargs.get("top_k", 4)
@ -205,20 +222,25 @@ class OracleVector(BaseVector):
if document_ids_filter: if document_ids_filter:
document_ids = ", ".join(f"'{id}'" for id in document_ids_filter) document_ids = ", ".join(f"'{id}'" for id in document_ids_filter)
where_clause = f"WHERE metadata->>'document_id' in ({document_ids})" where_clause = f"WHERE metadata->>'document_id' in ({document_ids})"
with self._get_cursor() as cur: with self._get_connection() as conn:
cur.execute( conn.inputtypehandler = self.input_type_handler
f"SELECT meta, text, vector_distance(embedding,:1) AS distance FROM {self.table_name}" conn.outputtypehandler = self.output_type_handler
f" {where_clause} ORDER BY distance fetch first {top_k} rows only", with conn.cursor() as cur:
[numpy.array(query_vector)], cur.execute(
) f"""SELECT meta, text, vector_distance(embedding,(select to_vector(:1) from dual),cosine)
docs = [] AS distance FROM {self.table_name}
score_threshold = float(kwargs.get("score_threshold") or 0.0) {where_clause} ORDER BY distance fetch first {top_k} rows only""",
for record in cur: [numpy.array(query_vector)],
metadata, text, distance = record )
score = 1 - distance docs = []
metadata["score"] = score score_threshold = float(kwargs.get("score_threshold") or 0.0)
if score > score_threshold: for record in cur:
docs.append(Document(page_content=text, metadata=metadata)) metadata, text, distance = record
score = 1 - distance
metadata["score"] = score
if score > score_threshold:
docs.append(Document(page_content=text, metadata=metadata))
conn.close()
return docs return docs
def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]:
@ -228,7 +250,7 @@ class OracleVector(BaseVector):
top_k = kwargs.get("top_k", 5) top_k = kwargs.get("top_k", 5)
# just not implement fetch by score_threshold now, may be later # just not implement fetch by score_threshold now, may be later
# score_threshold = float(kwargs.get("score_threshold") or 0.0) score_threshold = float(kwargs.get("score_threshold") or 0.0)
if len(query) > 0: if len(query) > 0:
# Check which language the query is in # Check which language the query is in
zh_pattern = re.compile("[\u4e00-\u9fa5]+") zh_pattern = re.compile("[\u4e00-\u9fa5]+")
@ -239,7 +261,7 @@ class OracleVector(BaseVector):
words = pseg.cut(query) words = pseg.cut(query)
current_entity = "" current_entity = ""
for word, pos in words: for word, pos in words:
if pos in {"nr", "Ng", "eng", "nz", "n", "ORG", "v"}: # nr: 人名ns: 地名,nt: 机构名 if pos in {"nr", "Ng", "eng", "nz", "n", "ORG", "v"}: # nr: 人名, ns: 地名, nt: 机构名
current_entity += word current_entity += word
else: else:
if current_entity: if current_entity:
@ -260,30 +282,35 @@ class OracleVector(BaseVector):
for token in all_tokens: for token in all_tokens:
if token not in stop_words: if token not in stop_words:
entities.append(token) entities.append(token)
with self._get_cursor() as cur: with self._get_connection() as conn:
document_ids_filter = kwargs.get("document_ids_filter") with conn.cursor() as cur:
where_clause = "" document_ids_filter = kwargs.get("document_ids_filter")
if document_ids_filter: where_clause = ""
document_ids = ", ".join(f"'{id}'" for id in document_ids_filter) if document_ids_filter:
where_clause = f" AND metadata->>'document_id' in ({document_ids}) " document_ids = ", ".join(f"'{id}'" for id in document_ids_filter)
cur.execute( where_clause = f" AND metadata->>'document_id' in ({document_ids}) "
f"select meta, text, embedding FROM {self.table_name}" cur.execute(
f"WHERE CONTAINS(text, :1, 1) > 0 {where_clause} " f"""select meta, text, embedding FROM {self.table_name}
f"order by score(1) desc fetch first {top_k} rows only", WHERE CONTAINS(text, :kk, 1) > 0 {where_clause}
[" ACCUM ".join(entities)], order by score(1) desc fetch first {top_k} rows only""",
) kk=" ACCUM ".join(entities),
docs = [] )
for record in cur: docs = []
metadata, text, embedding = record for record in cur:
docs.append(Document(page_content=text, vector=embedding, metadata=metadata)) metadata, text, embedding = record
docs.append(Document(page_content=text, vector=embedding, metadata=metadata))
conn.close()
return docs return docs
else: else:
return [Document(page_content="", metadata={})] return [Document(page_content="", metadata={})]
return [] return []
def delete(self) -> None: def delete(self) -> None:
with self._get_cursor() as cur: with self._get_connection() as conn:
cur.execute(f"DROP TABLE IF EXISTS {self.table_name} cascade constraints") with conn.cursor() as cur:
cur.execute(f"DROP TABLE IF EXISTS {self.table_name} cascade constraints")
conn.commit()
conn.close()
def _create_collection(self, dimension: int): def _create_collection(self, dimension: int):
cache_key = f"vector_indexing_{self._collection_name}" cache_key = f"vector_indexing_{self._collection_name}"
@ -293,11 +320,14 @@ class OracleVector(BaseVector):
if redis_client.get(collection_exist_cache_key): if redis_client.get(collection_exist_cache_key):
return return
with self._get_cursor() as cur: with self._get_connection() as conn:
cur.execute(SQL_CREATE_TABLE.format(table_name=self.table_name)) with conn.cursor() as cur:
redis_client.set(collection_exist_cache_key, 1, ex=3600) cur.execute(SQL_CREATE_TABLE.format(table_name=self.table_name))
with self._get_cursor() as cur: redis_client.set(collection_exist_cache_key, 1, ex=3600)
cur.execute(SQL_CREATE_INDEX.format(table_name=self.table_name)) with conn.cursor() as cur:
cur.execute(SQL_CREATE_INDEX.format(table_name=self.table_name))
conn.commit()
conn.close()
class OracleVectorFactory(AbstractVectorFactory): class OracleVectorFactory(AbstractVectorFactory):

@ -94,7 +94,7 @@ class DatasetRetrieverTool(DatasetRetrieverBaseTool):
"title": item.metadata.get("title"), "title": item.metadata.get("title"),
"content": item.page_content, "content": item.page_content,
} }
context_list.append(source) context_list.append(source)
for hit_callback in self.hit_callbacks: for hit_callback in self.hit_callbacks:
hit_callback.return_retriever_resource_info(context_list) hit_callback.return_retriever_resource_info(context_list)

@ -245,6 +245,13 @@ class Workflow(Base):
@property @property
def tool_published(self) -> bool: def tool_published(self) -> bool:
"""
DEPRECATED: This property is not accurate for determining if a workflow is published as a tool.
It only checks if there's a WorkflowToolProvider for the app, not if this specific workflow version
is the one being used by the tool.
For accurate checking, use a direct query with tenant_id, app_id, and version.
"""
from models.tools import WorkflowToolProvider from models.tools import WorkflowToolProvider
return ( return (

@ -178,7 +178,7 @@ vdb = [
"couchbase~=4.3.0", "couchbase~=4.3.0",
"elasticsearch==8.14.0", "elasticsearch==8.14.0",
"opensearch-py==2.4.0", "opensearch-py==2.4.0",
"oracledb~=2.2.1", "oracledb==3.0.0",
"pgvecto-rs[sqlalchemy]~=0.2.1", "pgvecto-rs[sqlalchemy]~=0.2.1",
"pgvector==0.2.5", "pgvector==0.2.5",
"pymilvus~=2.5.0", "pymilvus~=2.5.0",

@ -28,6 +28,7 @@ from extensions.ext_database import db
from models.account import Account from models.account import Account
from models.enums import CreatedByRole from models.enums import CreatedByRole
from models.model import App, AppMode from models.model import App, AppMode
from models.tools import WorkflowToolProvider
from models.workflow import ( from models.workflow import (
Workflow, Workflow,
WorkflowNodeExecution, WorkflowNodeExecution,
@ -523,8 +524,19 @@ class WorkflowService:
# Cannot delete a workflow that's currently in use by an app # Cannot delete a workflow that's currently in use by an app
raise WorkflowInUseError(f"Cannot delete workflow that is currently in use by app '{app.name}'") raise WorkflowInUseError(f"Cannot delete workflow that is currently in use by app '{app.name}'")
# Check if this workflow is published as a tool # Don't use workflow.tool_published as it's not accurate for specific workflow versions
if workflow.tool_published: # Check if there's a tool provider using this specific workflow version
tool_provider = (
session.query(WorkflowToolProvider)
.filter(
WorkflowToolProvider.tenant_id == workflow.tenant_id,
WorkflowToolProvider.app_id == workflow.app_id,
WorkflowToolProvider.version == workflow.version,
)
.first()
)
if tool_provider:
# Cannot delete a workflow that's published as a tool # Cannot delete a workflow that's published as a tool
raise WorkflowInUseError("Cannot delete workflow that is published as a tool") raise WorkflowInUseError("Cannot delete workflow that is published as a tool")

@ -40,6 +40,10 @@ def workflow_setup():
def test_delete_workflow_success(workflow_setup): def test_delete_workflow_success(workflow_setup):
# Setup mocks # Setup mocks
# Mock the tool provider query to return None (not published as a tool)
workflow_setup["session"].query.return_value.filter.return_value.first.return_value = None
workflow_setup["session"].scalar = MagicMock( workflow_setup["session"].scalar = MagicMock(
side_effect=[workflow_setup["workflow"], None] side_effect=[workflow_setup["workflow"], None]
) # Return workflow first, then None for app ) # Return workflow first, then None for app
@ -97,7 +101,12 @@ def test_delete_workflow_in_use_by_app_error(workflow_setup):
def test_delete_workflow_published_as_tool_error(workflow_setup): def test_delete_workflow_published_as_tool_error(workflow_setup):
# Setup mocks # Setup mocks
workflow_setup["workflow"].tool_published = True from models.tools import WorkflowToolProvider
# Mock the tool provider query
mock_tool_provider = MagicMock(spec=WorkflowToolProvider)
workflow_setup["session"].query.return_value.filter.return_value.first.return_value = mock_tool_provider
workflow_setup["session"].scalar = MagicMock( workflow_setup["session"].scalar = MagicMock(
side_effect=[workflow_setup["workflow"], None] side_effect=[workflow_setup["workflow"], None]
) # Return workflow first, then None for app ) # Return workflow first, then None for app

@ -1471,7 +1471,7 @@ vdb = [
{ name = "couchbase", specifier = "~=4.3.0" }, { name = "couchbase", specifier = "~=4.3.0" },
{ name = "elasticsearch", specifier = "==8.14.0" }, { name = "elasticsearch", specifier = "==8.14.0" },
{ name = "opensearch-py", specifier = "==2.4.0" }, { name = "opensearch-py", specifier = "==2.4.0" },
{ name = "oracledb", specifier = "~=2.2.1" }, { name = "oracledb", specifier = "==3.0.0" },
{ name = "pgvecto-rs", extras = ["sqlalchemy"], specifier = "~=0.2.1" }, { name = "pgvecto-rs", extras = ["sqlalchemy"], specifier = "~=0.2.1" },
{ name = "pgvector", specifier = "==0.2.5" }, { name = "pgvector", specifier = "==0.2.5" },
{ name = "pymilvus", specifier = "~=2.5.0" }, { name = "pymilvus", specifier = "~=2.5.0" },
@ -3600,23 +3600,23 @@ wheels = [
[[package]] [[package]]
name = "oracledb" name = "oracledb"
version = "2.2.1" version = "3.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cryptography" }, { name = "cryptography" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/36/fb/3fbacb351833dd794abb184303a5761c4bb33df9d770fd15d01ead2ff738/oracledb-2.2.1.tar.gz", hash = "sha256:8464c6f0295f3318daf6c2c72c83c2dcbc37e13f8fd44e3e39ff8665f442d6b6", size = 580818 } sdist = { url = "https://files.pythonhosted.org/packages/bf/39/712f797b75705c21148fa1d98651f63c2e5cc6876e509a0a9e2f5b406572/oracledb-3.0.0.tar.gz", hash = "sha256:64dc86ee5c032febc556798b06e7b000ef6828bb0252084f6addacad3363db85", size = 840431 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/74/b7/a4238295944670fb8cc50a8cc082e0af5a0440bfb1c2bac2b18429c0a579/oracledb-2.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fb6d9a4d7400398b22edb9431334f9add884dec9877fd9c4ae531e1ccc6ee1fd", size = 3551303 }, { url = "https://files.pythonhosted.org/packages/fa/bf/d872c4b3fc15cd3261fe0ea72b21d181700c92dbc050160e161654987062/oracledb-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:52daa9141c63dfa75c07d445e9bb7f69f43bfb3c5a173ecc48c798fe50288d26", size = 4312963 },
{ url = "https://files.pythonhosted.org/packages/4f/5f/98481d44976cd2b3086361f2d50026066b24090b0e6cd1f2a12c824e9717/oracledb-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07757c240afbb4f28112a6affc2c5e4e34b8a92e5bb9af81a40fba398da2b028", size = 12258455 }, { url = "https://files.pythonhosted.org/packages/b1/ea/01ee29e76a610a53bb34fdc1030f04b7669c3f80b25f661e07850fc6160e/oracledb-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af98941789df4c6aaaf4338f5b5f6b7f2c8c3fe6f8d6a9382f177f350868747a", size = 2661536 },
{ url = "https://files.pythonhosted.org/packages/e9/54/06b2540286e2b63f60877d6f3c6c40747e216b6eeda0756260e194897076/oracledb-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63daec72f853c47179e98493e9b732909d96d495bdceb521c5973a3940d28142", size = 12317476 }, { url = "https://files.pythonhosted.org/packages/3d/8e/ad380e34a46819224423b4773e58c350bc6269643c8969604097ced8c3bc/oracledb-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9812bb48865aaec35d73af54cd1746679f2a8a13cbd1412ab371aba2e39b3943", size = 2867461 },
{ url = "https://files.pythonhosted.org/packages/4d/1a/67814439a4e24df83281a72cb0ba433d6b74e1bff52a9975b87a725bcba5/oracledb-2.2.1-cp311-cp311-win32.whl", hash = "sha256:fec5318d1e0ada7e4674574cb6c8d1665398e8b9c02982279107212f05df1660", size = 1369368 }, { url = "https://files.pythonhosted.org/packages/96/09/ecc4384a27fd6e1e4de824ae9c160e4ad3aaebdaade5b4bdcf56a4d1ff63/oracledb-3.0.0-cp311-cp311-win32.whl", hash = "sha256:6c27fe0de64f2652e949eb05b3baa94df9b981a4a45fa7f8a991e1afb450c8e2", size = 1752046 },
{ url = "https://files.pythonhosted.org/packages/e3/b8/b2a8f0607be17f58ec6689ad5fd15c2956f4996c64547325e96439570edf/oracledb-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:5134dccb5a11bc755abf02fd49be6dc8141dfcae4b650b55d40509323d00b5c2", size = 1655035 }, { url = "https://files.pythonhosted.org/packages/62/e8/f34bde24050c6e55eeba46b23b2291f2dd7fd272fa8b322dcbe71be55778/oracledb-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:f922709672002f0b40997456f03a95f03e5712a86c61159951c5ce09334325e0", size = 2101210 },
{ url = "https://files.pythonhosted.org/packages/24/5b/2fff762243030f31a6b1561fc8eeb142e69ba6ebd3e7fbe4a2c82f0eb6f0/oracledb-2.2.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ac5716bc9a48247fdf563f5f4ec097f5c9f074a60fd130cdfe16699208ca29b5", size = 3583960 }, { url = "https://files.pythonhosted.org/packages/6f/fc/24590c3a3d41e58494bd3c3b447a62835138e5f9b243d9f8da0cfb5da8dc/oracledb-3.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:acd0e747227dea01bebe627b07e958bf36588a337539f24db629dc3431d3f7eb", size = 4351993 },
{ url = "https://files.pythonhosted.org/packages/e6/88/34117ae830e7338af7c0481f1c0fc6eda44d558e12f9203b45b491e53071/oracledb-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c150bddb882b7c73fb462aa2d698744da76c363e404570ed11d05b65811d96c3", size = 11749006 }, { url = "https://files.pythonhosted.org/packages/b7/b6/1f3b0b7bb94d53e8857d77b2e8dbdf6da091dd7e377523e24b79dac4fd71/oracledb-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8b402f77c22af031cd0051aea2472ecd0635c1b452998f511aa08b7350c90a4", size = 2532640 },
{ url = "https://files.pythonhosted.org/packages/9d/58/bac788f18c21f727955652fe238de2d24a12c2b455ed4db18a6d23ff781e/oracledb-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193e1888411bc21187ade4b16b76820bd1e8f216e25602f6cd0a97d45723c1dc", size = 11950663 }, { url = "https://files.pythonhosted.org/packages/72/1a/1815f6c086ab49c00921cf155ff5eede5267fb29fcec37cb246339a5ce4d/oracledb-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:378a27782e9a37918bd07a5a1427a77cb6f777d0a5a8eac9c070d786f50120ef", size = 2765949 },
{ url = "https://files.pythonhosted.org/packages/3b/e2/005f66ae919c6f7c73e06863256cf43aa844330e2dc61a5f9779ae44a801/oracledb-2.2.1-cp312-cp312-win32.whl", hash = "sha256:44a960f8bbb0711af222e0a9690e037b6a2a382e0559ae8eeb9cfafe26c7a3bc", size = 1324255 }, { url = "https://files.pythonhosted.org/packages/33/8d/208900f8d372909792ee70b2daad3f7361181e55f2217c45ed9dff658b54/oracledb-3.0.0-cp312-cp312-win32.whl", hash = "sha256:54a28c2cb08316a527cd1467740a63771cc1c1164697c932aa834c0967dc4efc", size = 1709373 },
{ url = "https://files.pythonhosted.org/packages/e6/25/759eb2143134513382e66d874c4aacfd691dec3fef7141170cfa6c1b154f/oracledb-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:470136add32f0d0084225c793f12a52b61b52c3dc00c9cd388ec6a3db3a7643e", size = 1613047 }, { url = "https://files.pythonhosted.org/packages/0c/5e/c21754f19c896102793c3afec2277e2180aa7d505e4d7fcca24b52d14e4f/oracledb-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8289bad6d103ce42b140e40576cf0c81633e344d56e2d738b539341eacf65624", size = 2056452 },
] ]
[[package]] [[package]]

@ -29,6 +29,8 @@ import { useChildSegmentListKey, useSegmentListKey } from '@/service/knowledge/u
import useEditDocumentMetadata from '../metadata/hooks/use-edit-dataset-metadata' import useEditDocumentMetadata from '../metadata/hooks/use-edit-dataset-metadata'
import DatasetMetadataDrawer from '../metadata/metadata-dataset/dataset-metadata-drawer' import DatasetMetadataDrawer from '../metadata/metadata-dataset/dataset-metadata-drawer'
import StatusWithAction from '../common/document-status-with-action/status-with-action' import StatusWithAction from '../common/document-status-with-action/status-with-action'
import { LanguagesSupported } from '@/i18n/language'
import { getLocaleOnClient } from '@/i18n'
const FolderPlusIcon = ({ className }: React.SVGProps<SVGElement>) => { const FolderPlusIcon = ({ className }: React.SVGProps<SVGElement>) => {
return <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}> return <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
@ -98,7 +100,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
const isDataSourceWeb = dataset?.data_source_type === DataSourceType.WEB const isDataSourceWeb = dataset?.data_source_type === DataSourceType.WEB
const isDataSourceFile = dataset?.data_source_type === DataSourceType.FILE const isDataSourceFile = dataset?.data_source_type === DataSourceType.FILE
const embeddingAvailable = !!dataset?.embedding_available const embeddingAvailable = !!dataset?.embedding_available
const locale = getLocaleOnClient()
const debouncedSearchValue = useDebounce(searchValue, { wait: 500 }) const debouncedSearchValue = useDebounce(searchValue, { wait: 500 })
const { data: documentsRes, isFetching: isListLoading } = useDocumentList({ const { data: documentsRes, isFetching: isListLoading } = useDocumentList({
@ -260,7 +262,12 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
<a <a
className='flex items-center text-text-accent' className='flex items-center text-text-accent'
target='_blank' target='_blank'
href='https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'> href={
locale === LanguagesSupported[1]
? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
: 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
}
>
<span>{t('datasetDocuments.list.learnMore')}</span> <span>{t('datasetDocuments.list.learnMore')}</span>
<RiExternalLinkLine className='h-3 w-3' /> <RiExternalLinkLine className='h-3 w-3' />
</a> </a>

@ -57,7 +57,14 @@ const inputVarTypeToVarType = (type: InputVarType): VarType => {
} as any)[type] || VarType.string } as any)[type] || VarType.string
} }
const structTypeToVarType = (type: Type): VarType => { const structTypeToVarType = (type: Type, isArray?: boolean): VarType => {
if (isArray) {
return ({
[Type.string]: VarType.arrayString,
[Type.number]: VarType.arrayNumber,
[Type.object]: VarType.arrayObject,
} as any)[type] || VarType.string
}
return ({ return ({
[Type.string]: VarType.string, [Type.string]: VarType.string,
[Type.number]: VarType.number, [Type.number]: VarType.number,
@ -82,9 +89,12 @@ const findExceptVarInStructuredProperties = (properties: Record<string, StructFi
Object.keys(properties).forEach((key) => { Object.keys(properties).forEach((key) => {
const item = properties[key] const item = properties[key]
const isObj = item.type === Type.object const isObj = item.type === Type.object
const isArray = item.type === Type.array
const arrayType = item.items?.type
if (!isObj && !filterVar({ if (!isObj && !filterVar({
variable: key, variable: key,
type: structTypeToVarType(item.type), type: structTypeToVarType(isArray ? arrayType! : item.type, isArray),
}, [key])) { }, [key])) {
delete properties[key] delete properties[key]
return return
@ -103,9 +113,11 @@ const findExceptVarInStructuredOutput = (structuredOutput: StructuredOutput, fil
Object.keys(properties).forEach((key) => { Object.keys(properties).forEach((key) => {
const item = properties[key] const item = properties[key]
const isObj = item.type === Type.object const isObj = item.type === Type.object
const isArray = item.type === Type.array
const arrayType = item.items?.type
if (!isObj && !filterVar({ if (!isObj && !filterVar({
variable: key, variable: key,
type: structTypeToVarType(item.type), type: structTypeToVarType(isArray ? arrayType! : item.type, isArray),
}, [key])) { }, [key])) {
delete properties[key] delete properties[key]
return return
@ -1314,9 +1326,12 @@ const varToValueSelectorList = (v: Var, parentValueSelector: ValueSelector, res:
} }
if (isStructuredOutput) { if (isStructuredOutput) {
Object.keys((v.children as StructuredOutput)?.schema?.properties || {}).forEach((key) => { Object.keys((v.children as StructuredOutput)?.schema?.properties || {}).forEach((key) => {
const type = (v.children as StructuredOutput)?.schema?.properties[key].type
const isArray = type === Type.array
const arrayType = (v.children as StructuredOutput)?.schema?.properties[key].items?.type
varToValueSelectorList({ varToValueSelectorList({
variable: key, variable: key,
type: structTypeToVarType((v.children as StructuredOutput)?.schema?.properties[key].type), type: structTypeToVarType(isArray ? arrayType! : type, isArray),
}, [...parentValueSelector, v.variable], res) }, [...parentValueSelector, v.variable], res)
}) })
} }

@ -25,6 +25,9 @@ export enum Type {
boolean = 'boolean', boolean = 'boolean',
object = 'object', object = 'object',
array = 'array', array = 'array',
arrayString = 'array[string]',
arrayNumber = 'array[number]',
arrayObject = 'array[object]',
} }
export enum ArrayType { export enum ArrayType {

@ -123,7 +123,7 @@ const ChatVariableModal = ({
case ChatVarType.Number: case ChatVarType.Number:
return value || 0 return value || 0
case ChatVarType.Object: case ChatVarType.Object:
return formatValueFromObject(objectValue) return editInJSON ? value : formatValueFromObject(objectValue)
case ChatVarType.ArrayString: case ChatVarType.ArrayString:
case ChatVarType.ArrayNumber: case ChatVarType.ArrayNumber:
case ChatVarType.ArrayObject: case ChatVarType.ArrayObject:

@ -80,8 +80,8 @@ const translation = {
configureApp: 'アプリを設定する', configureApp: 'アプリを設定する',
endpointDeleteContent: '{{name}}を削除しますか?', endpointDeleteContent: '{{name}}を削除しますか?',
actionNum: '{{num}} {{action}} が含まれています', actionNum: '{{num}} {{action}} が含まれています',
endpointsDocLink: '文書を表示する', endpointsDocLink: 'ドキュメントを表示する',
switchVersion: 'スイッチ版', switchVersion: 'バージョンの切り替え',
}, },
debugInfo: { debugInfo: {
title: 'デバッグ', title: 'デバッグ',
@ -134,7 +134,7 @@ const translation = {
install: 'インストール', install: 'インストール',
dropPluginToInstall: 'プラグインパッケージをここにドロップしてインストールします', dropPluginToInstall: 'プラグインパッケージをここにドロップしてインストールします',
installPlugin: 'プラグインをインストールする', installPlugin: 'プラグインをインストールする',
back: 'バック', back: '戻る',
uploadingPackage: '{{packageName}}をアップロード中...', uploadingPackage: '{{packageName}}をアップロード中...',
}, },
installFromGitHub: { installFromGitHub: {
@ -191,7 +191,7 @@ const translation = {
installingWithError: '{{installingLength}}個のプラグインをインストール中、{{successLength}}件成功、{{errorLength}}件失敗', installingWithError: '{{installingLength}}個のプラグインをインストール中、{{successLength}}件成功、{{errorLength}}件失敗',
installing: '{{installingLength}}個のプラグインをインストール中、0個完了。', installing: '{{installingLength}}個のプラグインをインストール中、0個完了。',
}, },
from: 'から', from: 'インストール元',
install: '{{num}} インストール', install: '{{num}} インストール',
installAction: 'インストール', installAction: 'インストール',
installFrom: 'インストール元', installFrom: 'インストール元',

Loading…
Cancel
Save