Commit 2f13d277 authored by Joel's avatar Joel

Merge branch 'main' into feat/workflow

parents a36a2a10 3c182518
# Dify Open Source License # Open Source License
The Dify project is licensed under the Apache License 2.0, with the following additional conditions: Dify is licensed under the Apache License 2.0, with the following additional conditions:
1. Dify is permitted to be used for commercialization, such as using Dify as a "backend-as-a-service" for your other applications, or delivering it to enterprises as an application development platform. However, when the following conditions are met, you must contact the producer to obtain a commercial license: 1. Dify may be utilized commercially, including as a backend service for other applications or as an application development platform for enterprises. Should the conditions below be met, a commercial license must be obtained from the producer:
a. Multi-tenant SaaS service: Unless explicitly authorized by Dify in writing, you may not use the Dify.AI source code to operate a multi-tenant SaaS service that is similar to the Dify.AI service edition. a. Multi-tenant SaaS service: Unless explicitly authorized by Dify in writing, you may not use the Dify source code to operate a multi-tenant environment.
b. LOGO and copyright information: In the process of using Dify, you may not remove or modify the LOGO or copyright information in the Dify console. - Tenant Definition: Within the context of Dify, one tenant corresponds to one workspace. The workspace provides a separated area for each tenant's data and configurations.
b. LOGO and copyright information: In the process of using Dify's frontend components, you may not remove or modify the LOGO or copyright information in the Dify console or applications. This restriction is inapplicable to uses of Dify that do not involve its frontend components.
Please contact business@dify.ai by email to inquire about licensing matters. Please contact business@dify.ai by email to inquire about licensing matters.
2. As a contributor, you should agree that your contributed code: 2. As a contributor, you should agree that:
a. The producer can adjust the open-source agreement to be more strict or relaxed. a. The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.
b. Can be used for commercial purposes, such as Dify's cloud business. b. Your contributed code may be used for commercial purposes, including but not limited to its cloud business operations.
Apart from this, all other rights and restrictions follow the Apache License 2.0. If you need more detailed information, you can refer to the full version of Apache License 2.0. Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache License 2.0. Detailed information about the Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
The interactive design of this product is protected by appearance patent. The interactive design of this product is protected by appearance patent.
© 2023 LangGenius, Inc. © 2024 LangGenius, Inc.
---------- ----------
......
...@@ -15,7 +15,7 @@ from libs.rsa import generate_key_pair ...@@ -15,7 +15,7 @@ from libs.rsa import generate_key_pair
from models.account import Tenant from models.account import Tenant
from models.dataset import Dataset, DatasetCollectionBinding, DocumentSegment from models.dataset import Dataset, DatasetCollectionBinding, DocumentSegment
from models.dataset import Document as DatasetDocument from models.dataset import Document as DatasetDocument
from models.model import Account from models.model import Account, App, AppAnnotationSetting, MessageAnnotation
from models.provider import Provider, ProviderModel from models.provider import Provider, ProviderModel
...@@ -125,7 +125,114 @@ def reset_encrypt_key_pair(): ...@@ -125,7 +125,114 @@ def reset_encrypt_key_pair():
@click.command('vdb-migrate', help='migrate vector db.') @click.command('vdb-migrate', help='migrate vector db.')
def vdb_migrate(): @click.option('--scope', default='all', prompt=False, help='The scope of vector database to migrate, Default is All.')
def vdb_migrate(scope: str):
if scope in ['knowledge', 'all']:
migrate_knowledge_vector_database()
if scope in ['annotation', 'all']:
migrate_annotation_vector_database()
def migrate_annotation_vector_database():
"""
Migrate annotation datas to target vector database .
"""
click.echo(click.style('Start migrate annotation data.', fg='green'))
create_count = 0
skipped_count = 0
total_count = 0
page = 1
while True:
try:
# get apps info
apps = db.session.query(App).filter(
App.status == 'normal'
).order_by(App.created_at.desc()).paginate(page=page, per_page=50)
except NotFound:
break
page += 1
for app in apps:
total_count = total_count + 1
click.echo(f'Processing the {total_count} app {app.id}. '
+ f'{create_count} created, {skipped_count} skipped.')
try:
click.echo('Create app annotation index: {}'.format(app.id))
app_annotation_setting = db.session.query(AppAnnotationSetting).filter(
AppAnnotationSetting.app_id == app.id
).first()
if not app_annotation_setting:
skipped_count = skipped_count + 1
click.echo('App annotation setting is disabled: {}'.format(app.id))
continue
# get dataset_collection_binding info
dataset_collection_binding = db.session.query(DatasetCollectionBinding).filter(
DatasetCollectionBinding.id == app_annotation_setting.collection_binding_id
).first()
if not dataset_collection_binding:
click.echo('App annotation collection binding is not exist: {}'.format(app.id))
continue
annotations = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app.id).all()
dataset = Dataset(
id=app.id,
tenant_id=app.tenant_id,
indexing_technique='high_quality',
embedding_model_provider=dataset_collection_binding.provider_name,
embedding_model=dataset_collection_binding.model_name,
collection_binding_id=dataset_collection_binding.id
)
documents = []
if annotations:
for annotation in annotations:
document = Document(
page_content=annotation.question,
metadata={
"annotation_id": annotation.id,
"app_id": app.id,
"doc_id": annotation.id
}
)
documents.append(document)
vector = Vector(dataset, attributes=['doc_id', 'annotation_id', 'app_id'])
click.echo(f"Start to migrate annotation, app_id: {app.id}.")
try:
vector.delete()
click.echo(
click.style(f'Successfully delete vector index for app: {app.id}.',
fg='green'))
except Exception as e:
click.echo(
click.style(f'Failed to delete vector index for app {app.id}.',
fg='red'))
raise e
if documents:
try:
click.echo(click.style(
f'Start to created vector index with {len(documents)} annotations for app {app.id}.',
fg='green'))
vector.create(documents)
click.echo(
click.style(f'Successfully created vector index for app {app.id}.', fg='green'))
except Exception as e:
click.echo(click.style(f'Failed to created vector index for app {app.id}.', fg='red'))
raise e
click.echo(f'Successfully migrated app annotation {app.id}.')
create_count += 1
except Exception as e:
click.echo(
click.style('Create app annotation index error: {} {}'.format(e.__class__.__name__, str(e)),
fg='red'))
continue
click.echo(
click.style(f'Congratulations! Create {create_count} app annotation indexes, and skipped {skipped_count} apps.',
fg='green'))
def migrate_knowledge_vector_database():
""" """
Migrate vector database datas to target vector database . Migrate vector database datas to target vector database .
""" """
......
...@@ -129,7 +129,7 @@ class AppListApi(Resource): ...@@ -129,7 +129,7 @@ class AppListApi(Resource):
"No Default System Reasoning Model available. Please configure " "No Default System Reasoning Model available. Please configure "
"in the Settings -> Model Provider.") "in the Settings -> Model Provider.")
else: else:
model_config_dict["model"]["provider"] = default_model_entity.provider model_config_dict["model"]["provider"] = default_model_entity.provider.provider
model_config_dict["model"]["name"] = default_model_entity.model model_config_dict["model"]["name"] = default_model_entity.model
model_configuration = AppModelConfigService.validate_configuration( model_configuration = AppModelConfigService.validate_configuration(
......
...@@ -259,6 +259,7 @@ class ToolApiProviderPreviousTestApi(Resource): ...@@ -259,6 +259,7 @@ class ToolApiProviderPreviousTestApi(Resource):
parser = reqparse.RequestParser() parser = reqparse.RequestParser()
parser.add_argument('tool_name', type=str, required=True, nullable=False, location='json') parser.add_argument('tool_name', type=str, required=True, nullable=False, location='json')
parser.add_argument('provider_name', type=str, required=False, nullable=False, location='json')
parser.add_argument('credentials', type=dict, required=True, nullable=False, location='json') parser.add_argument('credentials', type=dict, required=True, nullable=False, location='json')
parser.add_argument('parameters', type=dict, required=True, nullable=False, location='json') parser.add_argument('parameters', type=dict, required=True, nullable=False, location='json')
parser.add_argument('schema_type', type=str, required=True, nullable=False, location='json') parser.add_argument('schema_type', type=str, required=True, nullable=False, location='json')
...@@ -268,6 +269,7 @@ class ToolApiProviderPreviousTestApi(Resource): ...@@ -268,6 +269,7 @@ class ToolApiProviderPreviousTestApi(Resource):
return ToolManageService.test_api_tool_preview( return ToolManageService.test_api_tool_preview(
current_user.current_tenant_id, current_user.current_tenant_id,
args['provider_name'] if args['provider_name'] else '',
args['tool_name'], args['tool_name'],
args['credentials'], args['credentials'],
args['parameters'], args['parameters'],
......
...@@ -84,7 +84,7 @@ class AppRunner: ...@@ -84,7 +84,7 @@ class AppRunner:
return rest_tokens return rest_tokens
def recale_llm_max_tokens(self, model_config: ModelConfigEntity, def recalc_llm_max_tokens(self, model_config: ModelConfigEntity,
prompt_messages: list[PromptMessage]): prompt_messages: list[PromptMessage]):
# recalc max_tokens if sum(prompt_token + max_tokens) over model token limit # recalc max_tokens if sum(prompt_token + max_tokens) over model token limit
model_type_instance = model_config.provider_model_bundle.model_type_instance model_type_instance = model_config.provider_model_bundle.model_type_instance
......
...@@ -181,7 +181,7 @@ class BasicApplicationRunner(AppRunner): ...@@ -181,7 +181,7 @@ class BasicApplicationRunner(AppRunner):
return return
# Re-calculate the max tokens if sum(prompt_token + max_tokens) over model token limit # Re-calculate the max tokens if sum(prompt_token + max_tokens) over model token limit
self.recale_llm_max_tokens( self.recalc_llm_max_tokens(
model_config=app_orchestration_config.model_config, model_config=app_orchestration_config.model_config,
prompt_messages=prompt_messages prompt_messages=prompt_messages
) )
......
...@@ -130,8 +130,8 @@ class AssistantCotApplicationRunner(BaseAssistantApplicationRunner): ...@@ -130,8 +130,8 @@ class AssistantCotApplicationRunner(BaseAssistantApplicationRunner):
input=query input=query
) )
# recale llm max tokens # recalc llm max tokens
self.recale_llm_max_tokens(self.model_config, prompt_messages) self.recalc_llm_max_tokens(self.model_config, prompt_messages)
# invoke model # invoke model
chunks: Generator[LLMResultChunk, None, None] = model_instance.invoke_llm( chunks: Generator[LLMResultChunk, None, None] = model_instance.invoke_llm(
prompt_messages=prompt_messages, prompt_messages=prompt_messages,
......
...@@ -105,8 +105,8 @@ class AssistantFunctionCallApplicationRunner(BaseAssistantApplicationRunner): ...@@ -105,8 +105,8 @@ class AssistantFunctionCallApplicationRunner(BaseAssistantApplicationRunner):
messages_ids=message_file_ids messages_ids=message_file_ids
) )
# recale llm max tokens # recalc llm max tokens
self.recale_llm_max_tokens(self.model_config, prompt_messages) self.recalc_llm_max_tokens(self.model_config, prompt_messages)
# invoke model # invoke model
chunks: Union[Generator[LLMResultChunk, None, None], LLMResult] = model_instance.invoke_llm( chunks: Union[Generator[LLMResultChunk, None, None], LLMResult] = model_instance.invoke_llm(
prompt_messages=prompt_messages, prompt_messages=prompt_messages,
......
...@@ -2,7 +2,7 @@ provider: jina ...@@ -2,7 +2,7 @@ provider: jina
label: label:
en_US: Jina en_US: Jina
description: description:
en_US: Embedding Model Supported en_US: Embedding and Rerank Model Supported
icon_small: icon_small:
en_US: icon_s_en.svg en_US: icon_s_en.svg
icon_large: icon_large:
...@@ -13,9 +13,10 @@ help: ...@@ -13,9 +13,10 @@ help:
en_US: Get your API key from Jina AI en_US: Get your API key from Jina AI
zh_Hans: 从 Jina 获取 API Key zh_Hans: 从 Jina 获取 API Key
url: url:
en_US: https://jina.ai/embeddings/ en_US: https://jina.ai/
supported_model_types: supported_model_types:
- text-embedding - text-embedding
- rerank
configurate_methods: configurate_methods:
- predefined-model - predefined-model
provider_credential_schema: provider_credential_schema:
......
model: jina-reranker-v1-base-en
model_type: rerank
model_properties:
context_size: 8192
from typing import Optional
import httpx
from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult
from core.model_runtime.errors.invoke import (
InvokeAuthorizationError,
InvokeBadRequestError,
InvokeConnectionError,
InvokeError,
InvokeRateLimitError,
InvokeServerUnavailableError,
)
from core.model_runtime.errors.validate import CredentialsValidateFailedError
from core.model_runtime.model_providers.__base.rerank_model import RerankModel
class JinaRerankModel(RerankModel):
"""
Model class for Jina rerank model.
"""
def _invoke(self, model: str, credentials: dict,
query: str, docs: list[str], score_threshold: Optional[float] = None, top_n: Optional[int] = None,
user: Optional[str] = None) -> RerankResult:
"""
Invoke rerank model
:param model: model name
:param credentials: model credentials
:param query: search query
:param docs: docs for reranking
:param score_threshold: score threshold
:param top_n: top n documents to return
:param user: unique user id
:return: rerank result
"""
if len(docs) == 0:
return RerankResult(model=model, docs=[])
try:
response = httpx.post(
"https://api.jina.ai/v1/rerank",
json={
"model": model,
"query": query,
"documents": docs,
"top_n": top_n
},
headers={"Authorization": f"Bearer {credentials.get('api_key')}"}
)
response.raise_for_status()
results = response.json()
rerank_documents = []
for result in results['results']:
rerank_document = RerankDocument(
index=result['index'],
text=result['document']['text'],
score=result['relevance_score'],
)
if score_threshold is None or result['relevance_score'] >= score_threshold:
rerank_documents.append(rerank_document)
return RerankResult(model=model, docs=rerank_documents)
except httpx.HTTPStatusError as e:
raise InvokeServerUnavailableError(str(e))
def validate_credentials(self, model: str, credentials: dict) -> None:
"""
Validate model credentials
:param model: model name
:param credentials: model credentials
:return:
"""
try:
self._invoke(
model=model,
credentials=credentials,
query="What is the capital of the United States?",
docs=[
"Carson City is the capital city of the American state of Nevada. At the 2010 United States "
"Census, Carson City had a population of 55,274.",
"The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that "
"are a political division controlled by the United States. Its capital is Saipan.",
],
score_threshold=0.8
)
except Exception as ex:
raise CredentialsValidateFailedError(str(ex))
@property
def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]:
"""
Map model invoke error to unified error
"""
return {
InvokeConnectionError: [httpx.ConnectError],
InvokeServerUnavailableError: [httpx.RemoteProtocolError],
InvokeRateLimitError: [],
InvokeAuthorizationError: [httpx.HTTPStatusError],
InvokeBadRequestError: [httpx.RequestError]
}
...@@ -140,7 +140,8 @@ class MilvusVector(BaseVector): ...@@ -140,7 +140,8 @@ class MilvusVector(BaseVector):
connections.connect(alias=alias, uri=uri, user=self._client_config.user, password=self._client_config.password) connections.connect(alias=alias, uri=uri, user=self._client_config.user, password=self._client_config.password)
from pymilvus import utility from pymilvus import utility
utility.drop_collection(self._collection_name, None, using=alias) if utility.has_collection(self._collection_name, using=alias):
utility.drop_collection(self._collection_name, None, using=alias)
def text_exists(self, id: str) -> bool: def text_exists(self, id: str) -> bool:
......
...@@ -231,21 +231,30 @@ class QdrantVector(BaseVector): ...@@ -231,21 +231,30 @@ class QdrantVector(BaseVector):
def delete(self): def delete(self):
from qdrant_client.http import models from qdrant_client.http import models
filter = models.Filter( from qdrant_client.http.exceptions import UnexpectedResponse
must=[
models.FieldCondition( try:
key="group_id", filter = models.Filter(
match=models.MatchValue(value=self._group_id), must=[
models.FieldCondition(
key="group_id",
match=models.MatchValue(value=self._group_id),
),
],
)
self._client.delete(
collection_name=self._collection_name,
points_selector=FilterSelector(
filter=filter
), ),
], )
) except UnexpectedResponse as e:
self._client.delete( # Collection does not exist, so return
collection_name=self._collection_name, if e.status_code == 404:
points_selector=FilterSelector( return
filter=filter # Some other error occurred, so re-raise the exception
), else:
) raise e
def delete_by_ids(self, ids: list[str]) -> None: def delete_by_ids(self, ids: list[str]) -> None:
from qdrant_client.http import models from qdrant_client.http import models
......
...@@ -16,7 +16,8 @@ class BingProvider(BuiltinToolProviderController): ...@@ -16,7 +16,8 @@ class BingProvider(BuiltinToolProviderController):
user_id='', user_id='',
tool_parameters={ tool_parameters={
"query": "test", "query": "test",
"result_type": "link" "result_type": "link",
"enable_webpages": True,
}, },
) )
except Exception as e: except Exception as e:
......
...@@ -2,7 +2,7 @@ from core.tools.provider.builtin.wecom.tools.wecom_group_bot import WecomReposit ...@@ -2,7 +2,7 @@ from core.tools.provider.builtin.wecom.tools.wecom_group_bot import WecomReposit
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
class GaodeProvider(BuiltinToolProviderController): class WecomProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None: def _validate_credentials(self, credentials: dict) -> None:
WecomRepositoriesTool() WecomRepositoriesTool()
pass pass
import json import json
from json import dumps from json import dumps
from typing import Any, Union from typing import Any, Union
from urllib.parse import urlencode
import httpx import httpx
import requests import requests
...@@ -203,6 +204,8 @@ class ApiTool(Tool): ...@@ -203,6 +204,8 @@ class ApiTool(Tool):
if 'Content-Type' in headers: if 'Content-Type' in headers:
if headers['Content-Type'] == 'application/json': if headers['Content-Type'] == 'application/json':
body = dumps(body) body = dumps(body)
elif headers['Content-Type'] == 'application/x-www-form-urlencoded':
body = urlencode(body)
else: else:
body = body body = body
......
...@@ -52,7 +52,7 @@ safetensors==0.3.2 ...@@ -52,7 +52,7 @@ safetensors==0.3.2
zhipuai==1.0.7 zhipuai==1.0.7
werkzeug~=3.0.1 werkzeug~=3.0.1
pymilvus==2.3.0 pymilvus==2.3.0
qdrant-client==1.6.4 qdrant-client==1.7.3
cohere~=4.44 cohere~=4.44
pyyaml~=6.0.1 pyyaml~=6.0.1
numpy~=1.25.2 numpy~=1.25.2
......
...@@ -498,12 +498,16 @@ class ToolManageService: ...@@ -498,12 +498,16 @@ class ToolManageService:
@staticmethod @staticmethod
def test_api_tool_preview( def test_api_tool_preview(
tenant_id: str, tool_name: str, credentials: dict, parameters: dict, schema_type: str, schema: str tenant_id: str,
provider_name: str,
tool_name: str,
credentials: dict,
parameters: dict,
schema_type: str,
schema: str
): ):
""" """
test api tool before adding api tool provider test api tool before adding api tool provider
1. parse schema into tool bundle
""" """
if schema_type not in [member.value for member in ApiProviderSchemaType]: if schema_type not in [member.value for member in ApiProviderSchemaType]:
raise ValueError(f'invalid schema type {schema_type}') raise ValueError(f'invalid schema type {schema_type}')
...@@ -518,15 +522,21 @@ class ToolManageService: ...@@ -518,15 +522,21 @@ class ToolManageService:
if tool_bundle is None: if tool_bundle is None:
raise ValueError(f'invalid tool name {tool_name}') raise ValueError(f'invalid tool name {tool_name}')
# create a fake db provider db_provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
db_provider = ApiToolProvider( ApiToolProvider.tenant_id == tenant_id,
tenant_id='', user_id='', name='', icon='', ApiToolProvider.name == provider_name,
schema=schema, ).first()
description='',
schema_type_str=ApiProviderSchemaType.OPENAPI.value, if not db_provider:
tools_str=serialize_base_model_array(tool_bundles), # create a fake db provider
credentials_str=json.dumps(credentials), db_provider = ApiToolProvider(
) tenant_id='', user_id='', name='', icon='',
schema=schema,
description='',
schema_type_str=ApiProviderSchemaType.OPENAPI.value,
tools_str=serialize_base_model_array(tool_bundles),
credentials_str=json.dumps(credentials),
)
if 'auth_type' not in credentials: if 'auth_type' not in credentials:
raise ValueError('auth_type is required') raise ValueError('auth_type is required')
...@@ -539,6 +549,19 @@ class ToolManageService: ...@@ -539,6 +549,19 @@ class ToolManageService:
# load tools into provider entity # load tools into provider entity
provider_controller.load_bundled_tools(tool_bundles) provider_controller.load_bundled_tools(tool_bundles)
# decrypt credentials
if db_provider.id:
tool_configuration = ToolConfiguration(
tenant_id=tenant_id,
provider_controller=provider_controller
)
decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
# check if the credential has changed, save the original credential
masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
for name, value in credentials.items():
if name in masked_credentials and value == masked_credentials[name]:
credentials[name] = decrypted_credentials[name]
try: try:
provider_controller.validate_credentials_format(credentials) provider_controller.validate_credentials_format(credentials)
# get tool # get tool
......
...@@ -20,7 +20,7 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' ...@@ -20,7 +20,7 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
const noDataIcon = ( const noDataIcon = (
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.4998 51.3333V39.6666M10.4998 16.3333V4.66663M4.6665 10.5H16.3332M4.6665 45.5H16.3332M30.3332 6.99996L26.2868 17.5206C25.6287 19.2315 25.2997 20.0869 24.7881 20.8065C24.3346 21.4442 23.7774 22.0014 23.1397 22.4549C22.4202 22.9665 21.5647 23.2955 19.8538 23.9535L9.33317 28L19.8539 32.0464C21.5647 32.7044 22.4202 33.0334 23.1397 33.5451C23.7774 33.9985 24.3346 34.5557 24.7881 35.1934C25.2997 35.913 25.6287 36.7684 26.2868 38.4793L30.3332 49L34.3796 38.4793C35.0376 36.7684 35.3666 35.913 35.8783 35.1934C36.3317 34.5557 36.8889 33.9985 37.5266 33.5451C38.2462 33.0334 39.1016 32.7044 40.8125 32.0464L51.3332 28L40.8125 23.9535C39.1016 23.2955 38.2462 22.9665 37.5266 22.4549C36.8889 22.0014 36.3317 21.4442 35.8783 20.8065C35.3666 20.0869 35.0376 19.2315 34.3796 17.5206L30.3332 6.99996Z" stroke="#EAECF0" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"/> <path d="M10.4998 51.3333V39.6666M10.4998 16.3333V4.66663M4.6665 10.5H16.3332M4.6665 45.5H16.3332M30.3332 6.99996L26.2868 17.5206C25.6287 19.2315 25.2997 20.0869 24.7881 20.8065C24.3346 21.4442 23.7774 22.0014 23.1397 22.4549C22.4202 22.9665 21.5647 23.2955 19.8538 23.9535L9.33317 28L19.8539 32.0464C21.5647 32.7044 22.4202 33.0334 23.1397 33.5451C23.7774 33.9985 24.3346 34.5557 24.7881 35.1934C25.2997 35.913 25.6287 36.7684 26.2868 38.4793L30.3332 49L34.3796 38.4793C35.0376 36.7684 35.3666 35.913 35.8783 35.1934C36.3317 34.5557 36.8889 33.9985 37.5266 33.5451C38.2462 33.0334 39.1016 32.7044 40.8125 32.0464L51.3332 28L40.8125 23.9535C39.1016 23.2955 38.2462 22.9665 37.5266 22.4549C36.8889 22.0014 36.3317 21.4442 35.8783 20.8065C35.3666 20.0869 35.0376 19.2315 34.3796 17.5206L30.3332 6.99996Z" stroke="#EAECF0" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
</svg> </svg>
) )
...@@ -33,9 +33,9 @@ export type IGetAutomaticResProps = { ...@@ -33,9 +33,9 @@ export type IGetAutomaticResProps = {
const genIcon = ( const genIcon = (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.6665 1.33332C3.6665 0.965133 3.36803 0.666656 2.99984 0.666656C2.63165 0.666656 2.33317 0.965133 2.33317 1.33332V2.33332H1.33317C0.964981 2.33332 0.666504 2.6318 0.666504 2.99999C0.666504 3.36818 0.964981 3.66666 1.33317 3.66666H2.33317V4.66666C2.33317 5.03485 2.63165 5.33332 2.99984 5.33332C3.36803 5.33332 3.6665 5.03485 3.6665 4.66666V3.66666H4.6665C5.03469 3.66666 5.33317 3.36818 5.33317 2.99999C5.33317 2.6318 5.03469 2.33332 4.6665 2.33332H3.6665V1.33332Z" fill="white"/> <path d="M3.6665 1.33332C3.6665 0.965133 3.36803 0.666656 2.99984 0.666656C2.63165 0.666656 2.33317 0.965133 2.33317 1.33332V2.33332H1.33317C0.964981 2.33332 0.666504 2.6318 0.666504 2.99999C0.666504 3.36818 0.964981 3.66666 1.33317 3.66666H2.33317V4.66666C2.33317 5.03485 2.63165 5.33332 2.99984 5.33332C3.36803 5.33332 3.6665 5.03485 3.6665 4.66666V3.66666H4.6665C5.03469 3.66666 5.33317 3.36818 5.33317 2.99999C5.33317 2.6318 5.03469 2.33332 4.6665 2.33332H3.6665V1.33332Z" fill="white" />
<path d="M3.6665 11.3333C3.6665 10.9651 3.36803 10.6667 2.99984 10.6667C2.63165 10.6667 2.33317 10.9651 2.33317 11.3333V12.3333H1.33317C0.964981 12.3333 0.666504 12.6318 0.666504 13C0.666504 13.3682 0.964981 13.6667 1.33317 13.6667H2.33317V14.6667C2.33317 15.0348 2.63165 15.3333 2.99984 15.3333C3.36803 15.3333 3.6665 15.0348 3.6665 14.6667V13.6667H4.6665C5.03469 13.6667 5.33317 13.3682 5.33317 13C5.33317 12.6318 5.03469 12.3333 4.6665 12.3333H3.6665V11.3333Z" fill="white"/> <path d="M3.6665 11.3333C3.6665 10.9651 3.36803 10.6667 2.99984 10.6667C2.63165 10.6667 2.33317 10.9651 2.33317 11.3333V12.3333H1.33317C0.964981 12.3333 0.666504 12.6318 0.666504 13C0.666504 13.3682 0.964981 13.6667 1.33317 13.6667H2.33317V14.6667C2.33317 15.0348 2.63165 15.3333 2.99984 15.3333C3.36803 15.3333 3.6665 15.0348 3.6665 14.6667V13.6667H4.6665C5.03469 13.6667 5.33317 13.3682 5.33317 13C5.33317 12.6318 5.03469 12.3333 4.6665 12.3333H3.6665V11.3333Z" fill="white" />
<path d="M9.28873 1.76067C9.18971 1.50321 8.94235 1.33332 8.6665 1.33332C8.39066 1.33332 8.1433 1.50321 8.04427 1.76067L6.88815 4.76658C6.68789 5.28727 6.62495 5.43732 6.53887 5.55838C6.4525 5.67986 6.34637 5.78599 6.2249 5.87236C6.10384 5.95844 5.95379 6.02137 5.43309 6.22164L2.42718 7.37776C2.16972 7.47678 1.99984 7.72414 1.99984 7.99999C1.99984 8.27584 2.16972 8.5232 2.42718 8.62222L5.43309 9.77834C5.95379 9.97861 6.10384 10.0415 6.2249 10.1276C6.34637 10.214 6.4525 10.3201 6.53887 10.4416C6.62495 10.5627 6.68789 10.7127 6.88816 11.2334L8.04427 14.2393C8.1433 14.4968 8.39066 14.6667 8.6665 14.6667C8.94235 14.6667 9.18971 14.4968 9.28873 14.2393L10.4449 11.2334C10.6451 10.7127 10.7081 10.5627 10.7941 10.4416C10.8805 10.3201 10.9866 10.214 11.1081 10.1276C11.2292 10.0415 11.3792 9.97861 11.8999 9.77834L14.9058 8.62222C15.1633 8.5232 15.3332 8.27584 15.3332 7.99999C15.3332 7.72414 15.1633 7.47678 14.9058 7.37776L11.8999 6.22164C11.3792 6.02137 11.2292 5.95844 11.1081 5.87236C10.9866 5.78599 10.8805 5.67986 10.7941 5.55838C10.7081 5.43732 10.6451 5.28727 10.4449 4.76658L9.28873 1.76067Z" fill="white"/> <path d="M9.28873 1.76067C9.18971 1.50321 8.94235 1.33332 8.6665 1.33332C8.39066 1.33332 8.1433 1.50321 8.04427 1.76067L6.88815 4.76658C6.68789 5.28727 6.62495 5.43732 6.53887 5.55838C6.4525 5.67986 6.34637 5.78599 6.2249 5.87236C6.10384 5.95844 5.95379 6.02137 5.43309 6.22164L2.42718 7.37776C2.16972 7.47678 1.99984 7.72414 1.99984 7.99999C1.99984 8.27584 2.16972 8.5232 2.42718 8.62222L5.43309 9.77834C5.95379 9.97861 6.10384 10.0415 6.2249 10.1276C6.34637 10.214 6.4525 10.3201 6.53887 10.4416C6.62495 10.5627 6.68789 10.7127 6.88816 11.2334L8.04427 14.2393C8.1433 14.4968 8.39066 14.6667 8.6665 14.6667C8.94235 14.6667 9.18971 14.4968 9.28873 14.2393L10.4449 11.2334C10.6451 10.7127 10.7081 10.5627 10.7941 10.4416C10.8805 10.3201 10.9866 10.214 11.1081 10.1276C11.2292 10.0415 11.3792 9.97861 11.8999 9.77834L14.9058 8.62222C15.1633 8.5232 15.3332 8.27584 15.3332 7.99999C15.3332 7.72414 15.1633 7.47678 14.9058 7.37776L11.8999 6.22164C11.3792 6.02137 11.2292 5.95844 11.1081 5.87236C10.9866 5.78599 10.8805 5.67986 10.7941 5.55838C10.7081 5.43732 10.6451 5.28727 10.4449 4.76658L9.28873 1.76067Z" fill="white" />
</svg> </svg>
) )
...@@ -74,14 +74,14 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({ ...@@ -74,14 +74,14 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
const [res, setRes] = React.useState<AutomaticRes | null>(null) const [res, setRes] = React.useState<AutomaticRes | null>(null)
const renderLoading = ( const renderLoading = (
<div className='grow flex flex-col items-center justify-center h-full space-y-3'> <div className='w-0 grow flex flex-col items-center justify-center h-full space-y-3'>
<Loading /> <Loading />
<div className='text-[13px] text-gray-400'>{t('appDebug.automatic.loading')}</div> <div className='text-[13px] text-gray-400'>{t('appDebug.automatic.loading')}</div>
</div> </div>
) )
const renderNoData = ( const renderNoData = (
<div className='grow flex flex-col items-center justify-center h-full space-y-3'> <div className='w-0 grow flex flex-col items-center px-8 justify-center h-full space-y-3'>
{noDataIcon} {noDataIcon}
<div className='text-[13px] text-gray-400'>{t('appDebug.automatic.noData')}</div> <div className='text-[13px] text-gray-400'>{t('appDebug.automatic.noData')}</div>
</div> </div>
...@@ -142,7 +142,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({ ...@@ -142,7 +142,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
<div className='text-[13px] font-normal text-gray-500'>{t('appDebug.automatic.description')}</div> <div className='text-[13px] font-normal text-gray-500'>{t('appDebug.automatic.description')}</div>
</div> </div>
{/* inputs */} {/* inputs */}
<div className='mt-12 space-y-5'> <div className='mt-2 space-y-5'>
<div className='space-y-2'> <div className='space-y-2'>
<div className='text-[13px] font-medium text-gray-900'>{t('appDebug.automatic.intendedAudience')}</div> <div className='text-[13px] font-medium text-gray-900'>{t('appDebug.automatic.intendedAudience')}</div>
<input className="w-full h-8 px-3 text-[13px] font-normal bg-gray-50 rounded-lg" placeholder={t('appDebug.automatic.intendedAudiencePlaceHolder') as string} value={audiences} onChange={e => setAudiences(e.target.value)} /> <input className="w-full h-8 px-3 text-[13px] font-normal bg-gray-50 rounded-lg" placeholder={t('appDebug.automatic.intendedAudiencePlaceHolder') as string} value={audiences} onChange={e => setAudiences(e.target.value)} />
...@@ -167,8 +167,8 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({ ...@@ -167,8 +167,8 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
</div>} </div>}
{(!isLoading && res) && ( {(!isLoading && res) && (
<div className='grow px-8 pt-6 h-full overflow-y-auto'> <div className='w-0 grow px-8 pt-6 h-full overflow-y-auto'>
<div className='mb-4 w-1/2 text-lg font-medium text-gray-900'>{t('appDebug.automatic.resTitle')}</div> <div className='mb-4 text-lg font-medium text-gray-900'>{t('appDebug.automatic.resTitle')}</div>
<ConfigPrompt <ConfigPrompt
mode={mode} mode={mode}
...@@ -196,7 +196,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({ ...@@ -196,7 +196,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
</div> </div>
)} )}
<div className='sticky bottom-0 flex justify-end right-0 py-4'> <div className='sticky bottom-0 flex justify-end right-0 py-4 bg-white'>
<Button onClick={onClose}>{t('common.operation.cancel')}</Button> <Button onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button type='primary' className='ml-2' onClick={() => { <Button type='primary' className='ml-2' onClick={() => {
setShowConfirmOverwrite(true) setShowConfirmOverwrite(true)
......
...@@ -13,6 +13,7 @@ import { fetchFileUploadConfig } from '@/service/common' ...@@ -13,6 +13,7 @@ import { fetchFileUploadConfig } from '@/service/common'
import { fetchSupportFileTypes } from '@/service/datasets' import { fetchSupportFileTypes } from '@/service/datasets'
import I18n from '@/context/i18n' import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n/language' import { LanguagesSupported } from '@/i18n/language'
import { IS_CE_EDITION } from '@/config'
const FILES_NUMBER_LIMIT = 20 const FILES_NUMBER_LIMIT = 20
...@@ -180,7 +181,7 @@ const FileUploader = ({ ...@@ -180,7 +181,7 @@ const FileUploader = ({
if (!files.length) if (!files.length)
return false return false
if (files.length + fileList.length > FILES_NUMBER_LIMIT) { if (files.length + fileList.length > FILES_NUMBER_LIMIT && !IS_CE_EDITION) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.filesNumber', { filesNumber: FILES_NUMBER_LIMIT }) }) notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.filesNumber', { filesNumber: FILES_NUMBER_LIMIT }) })
return false return false
} }
......
...@@ -2,10 +2,9 @@ ...@@ -2,10 +2,9 @@
import type { FC } from 'react' import type { FC } from 'react'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import produce from 'immer'
import { useDebounce, useGetState } from 'ahooks' import { useDebounce, useGetState } from 'ahooks'
import { clone } from 'lodash-es'
import cn from 'classnames' import cn from 'classnames'
import produce from 'immer'
import { LinkExternal02, Settings01 } from '../../base/icons/src/vender/line/general' import { LinkExternal02, Settings01 } from '../../base/icons/src/vender/line/general'
import type { Credential, CustomCollectionBackend, CustomParamSchema, Emoji } from '../types' import type { Credential, CustomCollectionBackend, CustomParamSchema, Emoji } from '../types'
import { AuthHeaderPrefix, AuthType } from '../types' import { AuthHeaderPrefix, AuthType } from '../types'
...@@ -116,14 +115,16 @@ const EditCustomCollectionModal: FC<Props> = ({ ...@@ -116,14 +115,16 @@ const EditCustomCollectionModal: FC<Props> = ({
const [isShowTestApi, setIsShowTestApi] = useState(false) const [isShowTestApi, setIsShowTestApi] = useState(false)
const handleSave = () => { const handleSave = () => {
const postData = clone(customCollection) // const postData = clone(customCollection)
delete postData.tools const postData = produce(customCollection, (draft) => {
delete draft.tools
if (postData.credentials.auth_type === AuthType.none) { if (draft.credentials.auth_type === AuthType.none) {
delete postData.credentials.api_key_header delete draft.credentials.api_key_header
delete postData.credentials.api_key_header_prefix delete draft.credentials.api_key_header_prefix
delete postData.credentials.api_key_value delete draft.credentials.api_key_value
} }
})
if (isAdd) { if (isAdd) {
onAdd?.(postData) onAdd?.(postData)
......
...@@ -42,6 +42,7 @@ const TestApi: FC<Props> = ({ ...@@ -42,6 +42,7 @@ const TestApi: FC<Props> = ({
delete credentials.api_key_value delete credentials.api_key_value
} }
const data = { const data = {
provider_name: customCollection.provider,
tool_name: toolName, tool_name: toolName,
credentials, credentials,
schema_type: customCollection.schema_type, schema_type: customCollection.schema_type,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment