diff --git a/api/controllers/service_api/__init__.py b/api/controllers/service_api/__init__.py index aba9e3ecbb5a47..1f562c5221cf4a 100644 --- a/api/controllers/service_api/__init__.py +++ b/api/controllers/service_api/__init__.py @@ -6,5 +6,6 @@ api = ExternalApi(bp) from . import index -from .app import app, audio, completion, conversation, file, message, workflow -from .dataset import dataset, document, hit_testing, segment, upload_file +from .app import annotation, app, audio, completion, conversation, file, message, workflow +from .dataset import dataset, document, hit_testing, segment +from .workspace import models diff --git a/api/controllers/service_api/app/annotation.py b/api/controllers/service_api/app/annotation.py new file mode 100644 index 00000000000000..3d7fa786991214 --- /dev/null +++ b/api/controllers/service_api/app/annotation.py @@ -0,0 +1,107 @@ +from flask import request +from flask_restful import Resource, marshal, marshal_with, reqparse # type: ignore +from werkzeug.exceptions import Forbidden + +from controllers.service_api import api +from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token +from extensions.ext_redis import redis_client +from fields.annotation_fields import ( + annotation_fields, +) +from libs.login import current_user +from models.model import App, EndUser +from services.annotation_service import AppAnnotationService + + +class AnnotationReplyActionApi(Resource): + @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.JSON)) + def post(self, app_model: App, end_user: EndUser, action): + parser = reqparse.RequestParser() + parser.add_argument("score_threshold", required=True, type=float, location="json") + parser.add_argument("embedding_provider_name", required=True, type=str, location="json") + parser.add_argument("embedding_model_name", required=True, type=str, location="json") + args = parser.parse_args() + if action == "enable": + result = AppAnnotationService.enable_app_annotation(args, app_model.id) + elif action == "disable": + result = AppAnnotationService.disable_app_annotation(app_model.id) + else: + raise ValueError("Unsupported annotation reply action") + return result, 200 + + +class AnnotationReplyActionStatusApi(Resource): + @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) + def get(self, app_model: App, end_user: EndUser, job_id, action): + job_id = str(job_id) + app_annotation_job_key = "{}_app_annotation_job_{}".format(action, str(job_id)) + cache_result = redis_client.get(app_annotation_job_key) + if cache_result is None: + raise ValueError("The job is not exist.") + + job_status = cache_result.decode() + error_msg = "" + if job_status == "error": + app_annotation_error_key = "{}_app_annotation_error_{}".format(action, str(job_id)) + error_msg = redis_client.get(app_annotation_error_key).decode() + + return {"job_id": job_id, "job_status": job_status, "error_msg": error_msg}, 200 + + +class AnnotationListApi(Resource): + @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) + def get(self, app_model: App, end_user: EndUser): + page = request.args.get("page", default=1, type=int) + limit = request.args.get("limit", default=20, type=int) + keyword = request.args.get("keyword", default="", type=str) + + annotation_list, total = AppAnnotationService.get_annotation_list_by_app_id(app_model.id, page, limit, keyword) + response = { + "data": marshal(annotation_list, annotation_fields), + "has_more": len(annotation_list) == limit, + "limit": limit, + "total": total, + "page": page, + } + return response, 200 + + @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.JSON)) + @marshal_with(annotation_fields) + def post(self, app_model: App, end_user: EndUser): + parser = reqparse.RequestParser() + parser.add_argument("question", required=True, type=str, location="json") + parser.add_argument("answer", required=True, type=str, location="json") + args = parser.parse_args() + annotation = AppAnnotationService.insert_app_annotation_directly(args, app_model.id) + return annotation + + +class AnnotationUpdateDeleteApi(Resource): + @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.JSON)) + @marshal_with(annotation_fields) + def post(self, app_model: App, end_user: EndUser, annotation_id): + if not current_user.is_editor: + raise Forbidden() + + annotation_id = str(annotation_id) + parser = reqparse.RequestParser() + parser.add_argument("question", required=True, type=str, location="json") + parser.add_argument("answer", required=True, type=str, location="json") + args = parser.parse_args() + annotation = AppAnnotationService.update_app_annotation_directly(args, app_model.id, annotation_id) + return annotation + + @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) + def delete(self, app_model: App, end_user: EndUser, annotation_id): + if not current_user.is_editor: + raise Forbidden() + + annotation_id = str(annotation_id) + AppAnnotationService.delete_app_annotation(app_model.id, annotation_id) + return {"result": "success"}, 200 + + +api.add_resource(AnnotationReplyActionApi, "/apps/annotation-reply/") +api.add_resource(AnnotationReplyActionStatusApi, "/apps/annotation-reply//status/") +api.add_resource(AnnotationListApi, "/apps/annotations") +api.add_resource(AnnotationUpdateDeleteApi, "/apps/annotations/") diff --git a/api/controllers/service_api/dataset/dataset.py b/api/controllers/service_api/dataset/dataset.py index 49acdd693a8431..8277c13da1ea59 100644 --- a/api/controllers/service_api/dataset/dataset.py +++ b/api/controllers/service_api/dataset/dataset.py @@ -1,6 +1,6 @@ from flask import request from flask_restful import marshal, reqparse # type: ignore -from werkzeug.exceptions import NotFound +from werkzeug.exceptions import Forbidden, NotFound import services.dataset_service from controllers.service_api import api @@ -11,7 +11,7 @@ from fields.dataset_fields import dataset_detail_fields from libs.login import current_user from models.dataset import Dataset, DatasetPermissionEnum -from services.dataset_service import DatasetService +from services.dataset_service import DatasetPermissionService, DatasetService def _validate_name(name): @@ -20,6 +20,12 @@ def _validate_name(name): return name +def _validate_description_length(description): + if len(description) > 400: + raise ValueError("Description cannot exceed 400 characters.") + return description + + class DatasetListApi(DatasetApiResource): """Resource for datasets.""" @@ -135,6 +141,145 @@ def post(self, tenant_id): class DatasetApi(DatasetApiResource): """Resource for dataset.""" + def get(self, _, dataset_id): + dataset_id_str = str(dataset_id) + dataset = DatasetService.get_dataset(dataset_id_str) + if dataset is None: + raise NotFound("Dataset not found.") + try: + DatasetService.check_dataset_permission(dataset, current_user) + except services.errors.account.NoPermissionError as e: + raise Forbidden(str(e)) + data = marshal(dataset, dataset_detail_fields) + if data.get("permission") == "partial_members": + part_users_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str) + data.update({"partial_member_list": part_users_list}) + + # check embedding setting + provider_manager = ProviderManager() + configurations = provider_manager.get_configurations(tenant_id=current_user.current_tenant_id) + + embedding_models = configurations.get_models(model_type=ModelType.TEXT_EMBEDDING, only_active=True) + + model_names = [] + for embedding_model in embedding_models: + model_names.append(f"{embedding_model.model}:{embedding_model.provider.provider}") + + if data["indexing_technique"] == "high_quality": + item_model = f"{data['embedding_model']}:{data['embedding_model_provider']}" + if item_model in model_names: + data["embedding_available"] = True + else: + data["embedding_available"] = False + else: + data["embedding_available"] = True + + if data.get("permission") == "partial_members": + part_users_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str) + data.update({"partial_member_list": part_users_list}) + + return data, 200 + + def patch(self, _, dataset_id): + dataset_id_str = str(dataset_id) + dataset = DatasetService.get_dataset(dataset_id_str) + if dataset is None: + raise NotFound("Dataset not found.") + + parser = reqparse.RequestParser() + parser.add_argument( + "name", + nullable=False, + help="type is required. Name must be between 1 to 40 characters.", + type=_validate_name, + ) + parser.add_argument("description", location="json", store_missing=False, type=_validate_description_length) + parser.add_argument( + "indexing_technique", + type=str, + location="json", + choices=Dataset.INDEXING_TECHNIQUE_LIST, + nullable=True, + help="Invalid indexing technique.", + ) + parser.add_argument( + "permission", + type=str, + location="json", + choices=(DatasetPermissionEnum.ONLY_ME, DatasetPermissionEnum.ALL_TEAM, DatasetPermissionEnum.PARTIAL_TEAM), + help="Invalid permission.", + ) + parser.add_argument("embedding_model", type=str, location="json", help="Invalid embedding model.") + parser.add_argument( + "embedding_model_provider", type=str, location="json", help="Invalid embedding model provider." + ) + parser.add_argument("retrieval_model", type=dict, location="json", help="Invalid retrieval model.") + parser.add_argument("partial_member_list", type=list, location="json", help="Invalid parent user list.") + + parser.add_argument( + "external_retrieval_model", + type=dict, + required=False, + nullable=True, + location="json", + help="Invalid external retrieval model.", + ) + + parser.add_argument( + "external_knowledge_id", + type=str, + required=False, + nullable=True, + location="json", + help="Invalid external knowledge id.", + ) + + parser.add_argument( + "external_knowledge_api_id", + type=str, + required=False, + nullable=True, + location="json", + help="Invalid external knowledge api id.", + ) + args = parser.parse_args() + data = request.get_json() + + # check embedding model setting + if data.get("indexing_technique") == "high_quality": + DatasetService.check_embedding_model_setting( + dataset.tenant_id, data.get("embedding_model_provider"), data.get("embedding_model") + ) + + # The role of the current user in the ta table must be admin, owner, editor, or dataset_operator + DatasetPermissionService.check_permission( + current_user, dataset, data.get("permission"), data.get("partial_member_list") + ) + + dataset = DatasetService.update_dataset(dataset_id_str, args, current_user) + + if dataset is None: + raise NotFound("Dataset not found.") + + result_data = marshal(dataset, dataset_detail_fields) + tenant_id = current_user.current_tenant_id + + if data.get("partial_member_list") and data.get("permission") == "partial_members": + DatasetPermissionService.update_partial_member_list( + tenant_id, dataset_id_str, data.get("partial_member_list") + ) + # clear partial member list when permission is only_me or all_team_members + elif ( + data.get("permission") == DatasetPermissionEnum.ONLY_ME + or data.get("permission") == DatasetPermissionEnum.ALL_TEAM + ): + DatasetPermissionService.clear_partial_member_list(dataset_id_str) + + partial_member_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str) + result_data.update({"partial_member_list": partial_member_list}) + + return result_data, 200 + def delete(self, _, dataset_id): """ Deletes a dataset given its ID. @@ -155,6 +300,7 @@ def delete(self, _, dataset_id): try: if DatasetService.delete_dataset(dataset_id_str, current_user): + DatasetPermissionService.clear_partial_member_list(dataset_id_str) return {"result": "success"}, 204 else: raise NotFound("Dataset not found.") diff --git a/api/controllers/service_api/workspace/models.py b/api/controllers/service_api/workspace/models.py new file mode 100644 index 00000000000000..373f8019f9ec2d --- /dev/null +++ b/api/controllers/service_api/workspace/models.py @@ -0,0 +1,21 @@ +from flask_login import current_user # type: ignore +from flask_restful import Resource # type: ignore + +from controllers.service_api import api +from controllers.service_api.wraps import validate_dataset_token +from core.model_runtime.utils.encoders import jsonable_encoder +from services.model_provider_service import ModelProviderService + + +class ModelProviderAvailableModelApi(Resource): + @validate_dataset_token + def get(self, _, model_type): + tenant_id = current_user.current_tenant_id + + model_provider_service = ModelProviderService() + models = model_provider_service.get_models_by_model_type(tenant_id=tenant_id, model_type=model_type) + + return jsonable_encoder({"data": models}) + + +api.add_resource(ModelProviderAvailableModelApi, "/workspaces/current/models/model-types/") diff --git a/api/controllers/service_api/wraps.py b/api/controllers/service_api/wraps.py index fc4cce487691c5..9a5d96c51ad1af 100644 --- a/api/controllers/service_api/wraps.py +++ b/api/controllers/service_api/wraps.py @@ -56,6 +56,27 @@ def decorated_view(*args, **kwargs): if tenant.status == TenantStatus.ARCHIVE: raise Forbidden("The workspace's status is archived.") + tenant_account_join = ( + db.session.query(Tenant, TenantAccountJoin) + .filter(Tenant.id == api_token.tenant_id) + .filter(TenantAccountJoin.tenant_id == Tenant.id) + .filter(TenantAccountJoin.role.in_(["owner"])) + .filter(Tenant.status == TenantStatus.NORMAL) + .one_or_none() + ) # TODO: only owner information is required, so only one is returned. + if tenant_account_join: + tenant, ta = tenant_account_join + account = Account.query.filter_by(id=ta.account_id).first() + # Login admin + if account: + account.current_tenant = tenant + current_app.login_manager._update_request_context_with_user(account) # type: ignore + user_logged_in.send(current_app._get_current_object(), user=_get_user()) # type: ignore + else: + raise Unauthorized("Tenant owner account does not exist.") + else: + raise Unauthorized("Tenant does not exist.") + kwargs["app_model"] = app_model if fetch_user_arg: diff --git a/web/app/(commonLayout)/datasets/template/template.en.mdx b/web/app/(commonLayout)/datasets/template/template.en.mdx index 3fa22a1620ed9e..be9c92d890938a 100644 --- a/web/app/(commonLayout)/datasets/template/template.en.mdx +++ b/web/app/(commonLayout)/datasets/template/template.en.mdx @@ -439,6 +439,195 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
+ + + + ### Query + + + Knowledge Base ID + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + ```json {{ title: 'Response' }} + { + "id": "eaedb485-95ac-4ffd-ab1e-18da6d676a2f", + "name": "Test Knowledge Base", + "description": "", + "provider": "vendor", + "permission": "only_me", + "data_source_type": null, + "indexing_technique": null, + "app_count": 0, + "document_count": 0, + "word_count": 0, + "created_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6", + "created_at": 1735620612, + "updated_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6", + "updated_at": 1735620612, + "embedding_model": null, + "embedding_model_provider": null, + "embedding_available": true, + "retrieval_model_dict": { + "search_method": "semantic_search", + "reranking_enable": false, + "reranking_mode": null, + "reranking_model": { + "reranking_provider_name": "", + "reranking_model_name": "" + }, + "weights": null, + "top_k": 2, + "score_threshold_enabled": false, + "score_threshold": null + }, + "tags": [], + "doc_form": null, + "external_knowledge_info": { + "external_knowledge_id": null, + "external_knowledge_api_id": null, + "external_knowledge_api_name": null, + "external_knowledge_api_endpoint": null + }, + "external_retrieval_model": { + "top_k": 2, + "score_threshold": 0.0, + "score_threshold_enabled": null + } + } + ``` + + + + +
+ + + + + ### Query + + + Knowledge Base ID + + + Index technique (optional) + - high_quality High quality + - economy Economy + + + Permission + - only_me Only me + - all_team_members All team members + - partial_members Partial members + + + Specified embedding model provider, must be set up in the system first, corresponding to the provider field(Optional) + + + Specified embedding model, corresponding to the model field(Optional) + + + Specified retrieval model, corresponding to the model field(Optional) + + + Partial member list(Optional) + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{"name": "Test Knowledge Base", "indexing_technique": "high_quality", "permission": "only_me",\ + "embedding_model_provider": "zhipuai", "embedding_model": "embedding-3", "retrieval_model": "", "partial_member_list": []}' + ``` + + + ```json {{ title: 'Response' }} + { + "id": "eaedb485-95ac-4ffd-ab1e-18da6d676a2f", + "name": "Test Knowledge Base", + "description": "", + "provider": "vendor", + "permission": "only_me", + "data_source_type": null, + "indexing_technique": "high_quality", + "app_count": 0, + "document_count": 0, + "word_count": 0, + "created_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6", + "created_at": 1735620612, + "updated_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6", + "updated_at": 1735622679, + "embedding_model": "embedding-3", + "embedding_model_provider": "zhipuai", + "embedding_available": null, + "retrieval_model_dict": { + "search_method": "semantic_search", + "reranking_enable": false, + "reranking_mode": null, + "reranking_model": { + "reranking_provider_name": "", + "reranking_model_name": "" + }, + "weights": null, + "top_k": 2, + "score_threshold_enabled": false, + "score_threshold": null + }, + "tags": [], + "doc_form": null, + "external_knowledge_info": { + "external_knowledge_id": null, + "external_knowledge_api_id": null, + "external_knowledge_api_name": null, + "external_knowledge_api_endpoint": null + }, + "external_retrieval_model": { + "top_k": 2, + "score_threshold": 0.0, + "score_threshold_enabled": null + }, + "partial_member_list": [] + } + ``` + + + + +
+ + + + + ### Query + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/workspaces/current/models/model-types/text-embedding' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + ``` + + + ```json {{ title: 'Response' }} + { + "data": [ + { + "provider": "zhipuai", + "label": { + "zh_Hans": "智谱 AI", + "en_US": "ZHIPU AI" + }, + "icon_small": { + "zh_Hans": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_small/zh_Hans", + "en_US": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_small/en_US" + }, + "icon_large": { + "zh_Hans": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_large/zh_Hans", + "en_US": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_large/en_US" + }, + "status": "active", + "models": [ + { + "model": "embedding-3", + "label": { + "zh_Hans": "embedding-3", + "en_US": "embedding-3" + }, + "model_type": "text-embedding", + "features": null, + "fetch_from": "predefined-model", + "model_properties": { + "context_size": 8192 + }, + "deprecated": false, + "status": "active", + "load_balancing_enabled": false + }, + { + "model": "embedding-2", + "label": { + "zh_Hans": "embedding-2", + "en_US": "embedding-2" + }, + "model_type": "text-embedding", + "features": null, + "fetch_from": "predefined-model", + "model_properties": { + "context_size": 8192 + }, + "deprecated": false, + "status": "active", + "load_balancing_enabled": false + }, + { + "model": "text_embedding", + "label": { + "zh_Hans": "text_embedding", + "en_US": "text_embedding" + }, + "model_type": "text-embedding", + "features": null, + "fetch_from": "predefined-model", + "model_properties": { + "context_size": 512 + }, + "deprecated": false, + "status": "active", + "load_balancing_enabled": false + } + ] + } + ] + } + ``` + + + + +
+ ### Error message diff --git a/web/app/(commonLayout)/datasets/template/template.zh.mdx b/web/app/(commonLayout)/datasets/template/template.zh.mdx index 334591743f931b..3e657dca063ad2 100644 --- a/web/app/(commonLayout)/datasets/template/template.zh.mdx +++ b/web/app/(commonLayout)/datasets/template/template.zh.mdx @@ -439,6 +439,195 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
+ + + + ### Query + + + 知识库 ID + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + ```json {{ title: 'Response' }} + { + "id": "eaedb485-95ac-4ffd-ab1e-18da6d676a2f", + "name": "Test Knowledge Base", + "description": "", + "provider": "vendor", + "permission": "only_me", + "data_source_type": null, + "indexing_technique": null, + "app_count": 0, + "document_count": 0, + "word_count": 0, + "created_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6", + "created_at": 1735620612, + "updated_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6", + "updated_at": 1735620612, + "embedding_model": null, + "embedding_model_provider": null, + "embedding_available": true, + "retrieval_model_dict": { + "search_method": "semantic_search", + "reranking_enable": false, + "reranking_mode": null, + "reranking_model": { + "reranking_provider_name": "", + "reranking_model_name": "" + }, + "weights": null, + "top_k": 2, + "score_threshold_enabled": false, + "score_threshold": null + }, + "tags": [], + "doc_form": null, + "external_knowledge_info": { + "external_knowledge_id": null, + "external_knowledge_api_id": null, + "external_knowledge_api_name": null, + "external_knowledge_api_endpoint": null + }, + "external_retrieval_model": { + "top_k": 2, + "score_threshold": 0.0, + "score_threshold_enabled": null + } + } + ``` + + + + +
+ + + + + ### Query + + + 知识库 ID + + + 索引模式(选填,建议填写) + - high_quality 高质量 + - economy 经济 + + + 权限(选填,默认 only_me) + - only_me 仅自己 + - all_team_members 所有团队成员 + - partial_members 部分团队成员 + + + 嵌入模型提供商(选填), 必须先在系统内设定好接入的模型,对应的是provider字段 + + + 嵌入模型(选填) + + + 检索模型(选填) + + + 部分团队成员 ID 列表(选填) + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{"name": "Test Knowledge Base", "indexing_technique": "high_quality", "permission": "only_me",\ + "embedding_model_provider": "zhipuai", "embedding_model": "embedding-3", "retrieval_model": "", "partial_member_list": []}' + ``` + + + ```json {{ title: 'Response' }} + { + "id": "eaedb485-95ac-4ffd-ab1e-18da6d676a2f", + "name": "Test Knowledge Base", + "description": "", + "provider": "vendor", + "permission": "only_me", + "data_source_type": null, + "indexing_technique": "high_quality", + "app_count": 0, + "document_count": 0, + "word_count": 0, + "created_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6", + "created_at": 1735620612, + "updated_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6", + "updated_at": 1735622679, + "embedding_model": "embedding-3", + "embedding_model_provider": "zhipuai", + "embedding_available": null, + "retrieval_model_dict": { + "search_method": "semantic_search", + "reranking_enable": false, + "reranking_mode": null, + "reranking_model": { + "reranking_provider_name": "", + "reranking_model_name": "" + }, + "weights": null, + "top_k": 2, + "score_threshold_enabled": false, + "score_threshold": null + }, + "tags": [], + "doc_form": null, + "external_knowledge_info": { + "external_knowledge_id": null, + "external_knowledge_api_id": null, + "external_knowledge_api_name": null, + "external_knowledge_api_endpoint": null + }, + "external_retrieval_model": { + "top_k": 2, + "score_threshold": 0.0, + "score_threshold_enabled": null + }, + "partial_member_list": [] + } + ``` + + + + +
+
+
+ + + + + ### Query + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/workspaces/current/models/model-types/text-embedding' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + ``` + + + ```json {{ title: 'Response' }} + { + "data": [ + { + "provider": "zhipuai", + "label": { + "zh_Hans": "智谱 AI", + "en_US": "ZHIPU AI" + }, + "icon_small": { + "zh_Hans": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_small/zh_Hans", + "en_US": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_small/en_US" + }, + "icon_large": { + "zh_Hans": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_large/zh_Hans", + "en_US": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_large/en_US" + }, + "status": "active", + "models": [ + { + "model": "embedding-3", + "label": { + "zh_Hans": "embedding-3", + "en_US": "embedding-3" + }, + "model_type": "text-embedding", + "features": null, + "fetch_from": "predefined-model", + "model_properties": { + "context_size": 8192 + }, + "deprecated": false, + "status": "active", + "load_balancing_enabled": false + }, + { + "model": "embedding-2", + "label": { + "zh_Hans": "embedding-2", + "en_US": "embedding-2" + }, + "model_type": "text-embedding", + "features": null, + "fetch_from": "predefined-model", + "model_properties": { + "context_size": 8192 + }, + "deprecated": false, + "status": "active", + "load_balancing_enabled": false + }, + { + "model": "text_embedding", + "label": { + "zh_Hans": "text_embedding", + "en_US": "text_embedding" + }, + "model_type": "text-embedding", + "features": null, + "fetch_from": "predefined-model", + "model_properties": { + "context_size": 512 + }, + "deprecated": false, + "status": "active", + "load_balancing_enabled": false + } + ] + } + ] + } + ``` + + +
diff --git a/web/app/components/develop/template/template.zh.mdx b/web/app/components/develop/template/template.zh.mdx index d8cabb536d4dc0..b8c20d6985a089 100755 --- a/web/app/components/develop/template/template.zh.mdx +++ b/web/app/components/develop/template/template.zh.mdx @@ -548,3 +548,304 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' +--- + + + + + ### Query + + + 页码 + + + 每页数量 + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/apps/annotations?page=1&limit=20' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "data": [ + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + ], + "has_more": false, + "limit": 20, + "total": 1, + "page": 1 + } + ``` + + + +--- + + + + + ### Query + + + 问题 + + + 答案内容 + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/apps/annotations' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "question": "What is your name?", + "answer": "I am Dify." + }' + ``` + + + + ```json {{ title: 'Response' }} + { + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + } + ``` + + + +--- + + + + + ### Query + + + 标注 ID + + + 问题 + + + 答案内容 + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/apps/annotations/{annotation_id}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "question": "What is your name?", + "answer": "I am Dify." + }' + ``` + + + + ```json {{ title: 'Response' }} + { + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + } + ``` + + + +--- + + + + + ### Query + + + 标注 ID + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request DELETE '${props.apiBaseUrl}/apps/annotations/{annotation_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + {"result": "success"} + ``` + + + +--- + + + + + ### Query + + + 动作,只能是 'enable' 或 'disable' + + + 指定的嵌入模型提供商, 必须先在系统内设定好接入的模型,对应的是provider字段 + + + 指定的嵌入模型,对应的是model字段 + + + 相似度阈值,当相似度大于该阈值时,系统会自动回复,否则不回复 + + + + + 嵌入模型的提供商和模型名称可以通过以下接口获取:v1/workspaces/current/models/model-types/text-embedding, 具体见:通过 API 维护知识库。 使用的Authorization是Dataset的API Token。 + + ```bash {{ title: 'cURL' }} + curl --location --request POST 'https://api.dify.ai/v1/apps/annotation-reply/{action}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "score_threshold": 0.9, + "embedding_provider_name": "zhipu", + "embedding_model_name": "embedding_3" + }' + ``` + + + + ```json {{ title: 'Response' }} + { + "job_id": "b15c8f68-1cf4-4877-bf21-ed7cf2011802", + "job_status": "waiting" + } + ``` + 该接口是异步执行,所以会返回一个job_id,通过查询job状态接口可以获取到最终的执行结果。 + + + +--- + + + + + ### Query + + + 动作,只能是 'enable' 或 'disable',并且必须和标注回复初始设置接口的动作一致 + + + 任务 ID,从标注回复初始设置接口返回的 job_id + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/apps/annotation-reply/{action}/status/{job_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "job_id": "b15c8f68-1cf4-4877-bf21-ed7cf2011802", + "job_status": "waiting", + "error_msg": "" + } + ``` + + + diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index e905e9e0c62aa7..94dbb66ed67585 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -1137,3 +1137,304 @@ Chat applications support session persistence, allowing previous chat history to +--- + + + + + ### Query + + + Page number + + + Number of items returned, default 20, range 1-100 + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/apps/annotations?page=1&limit=20' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "data": [ + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + ], + "has_more": false, + "limit": 20, + "total": 1, + "page": 1 + } + ``` + + + +--- + + + + + ### Query + + + Question + + + Answer + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/apps/annotations' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "question": "What is your name?", + "answer": "I am Dify." + }' + ``` + + + + ```json {{ title: 'Response' }} + { + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + } + ``` + + + +--- + + + + + ### Query + + + Annotation ID + + + Question + + + Answer + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/apps/annotations/{annotation_id}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "question": "What is your name?", + "answer": "I am Dify." + }' + ``` + + + + ```json {{ title: 'Response' }} + { + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + } + ``` + + + +--- + + + + + ### Query + + + Annotation ID + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request DELETE '${props.apiBaseUrl}/apps/annotations/{annotation_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + {"result": "success"} + ``` + + + +--- + + + + + ### Query + + + Action, can only be 'enable' or 'disable' + + + Specified embedding model provider, must be set up in the system first, corresponding to the provider field(Optional) + + + Specified embedding model, corresponding to the model field(Optional) + + + The similarity threshold for matching annotated replies. Only annotations with scores above this threshold will be recalled. + + + + + The provider and model name of the embedding model can be obtained through the following interface: v1/workspaces/current/models/model-types/text-embedding. For specific instructions, see: Maintain Knowledge Base via API. The Authorization used is the Dataset API Token. + + ```bash {{ title: 'cURL' }} + curl --location --request POST 'https://api.dify.ai/v1/apps/annotation-reply/{action}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "score_threshold": 0.9, + "embedding_provider_name": "zhipu", + "embedding_model_name": "embedding_3" + }' + ``` + + + + ```json {{ title: 'Response' }} + { + "job_id": "b15c8f68-1cf4-4877-bf21-ed7cf2011802", + "job_status": "waiting" + } + ``` + This interface is executed asynchronously, so it will return a job_id. You can get the final execution result by querying the job status interface. + + + +--- + + + + + ### Query + + + Action, can only be 'enable' or 'disable', must be the same as the action in the initial annotation reply settings interface + + + Job ID, obtained from the initial annotation reply settings interface + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/apps/annotation-reply/{action}/status/{job_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "job_id": "b15c8f68-1cf4-4877-bf21-ed7cf2011802", + "job_status": "waiting", + "error_msg": "" + } + ``` + + + diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx index 8d181d7e0b7581..25a0d02253a0fe 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -1159,3 +1159,306 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' +--- + +--- + + + + + ### Query + + + 页码 + + + 每页数量 + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/apps/annotations?page=1&limit=20' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "data": [ + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + ], + "has_more": false, + "limit": 20, + "total": 1, + "page": 1 + } + ``` + + + +--- + + + + + ### Query + + + 问题 + + + 答案内容 + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/apps/annotations' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "question": "What is your name?", + "answer": "I am Dify." + }' + ``` + + + + ```json {{ title: 'Response' }} + { + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + } + ``` + + + +--- + + + + + ### Query + + + 标注 ID + + + 问题 + + + 答案内容 + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/apps/annotations/{annotation_id}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "question": "What is your name?", + "answer": "I am Dify." + }' + ``` + + + + ```json {{ title: 'Response' }} + { + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + } + ``` + + + +--- + + + + + ### Query + + + 标注 ID + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request DELETE '${props.apiBaseUrl}/apps/annotations/{annotation_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + {"result": "success"} + ``` + + + +--- + + + + + ### Query + + + 动作,只能是 'enable' 或 'disable' + + + 指定的嵌入模型提供商, 必须先在系统内设定好接入的模型,对应的是provider字段 + + + 指定的嵌入模型,对应的是model字段 + + + 相似度阈值,当相似度大于该阈值时,系统会自动回复,否则不回复 + + + + + 嵌入模型的提供商和模型名称可以通过以下接口获取:v1/workspaces/current/models/model-types/text-embedding, 具体见:通过 API 维护知识库。 使用的Authorization是Dataset的API Token。 + + ```bash {{ title: 'cURL' }} + curl --location --request POST 'https://api.dify.ai/v1/apps/annotation-reply/{action}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "score_threshold": 0.9, + "embedding_provider_name": "zhipu", + "embedding_model_name": "embedding_3" + }' + ``` + + + + ```json {{ title: 'Response' }} + { + "job_id": "b15c8f68-1cf4-4877-bf21-ed7cf2011802", + "job_status": "waiting" + } + ``` + 该接口是异步执行,所以会返回一个job_id,通过查询job状态接口可以获取到最终的执行结果。 + + + +--- + + + + + ### Query + + + 动作,只能是 'enable' 或 'disable',并且必须和标注回复初始设置接口的动作一致 + + + 任务 ID,从标注回复初始设置接口返回的 job_id + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/apps/annotation-reply/{action}/status/{job_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "job_id": "b15c8f68-1cf4-4877-bf21-ed7cf2011802", + "job_status": "waiting", + "error_msg": "" + } + ``` + + + diff --git a/web/app/components/develop/template/template_chat.en.mdx b/web/app/components/develop/template/template_chat.en.mdx index 7de329a7a0ed0e..35fee3a9fdbcc3 100644 --- a/web/app/components/develop/template/template_chat.en.mdx +++ b/web/app/components/develop/template/template_chat.en.mdx @@ -1173,3 +1173,304 @@ Chat applications support session persistence, allowing previous chat history to +--- + + + + + ### Query + + + Page number + + + Number of items returned, default 20, range 1-100 + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/apps/annotations?page=1&limit=20' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "data": [ + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + ], + "has_more": false, + "limit": 20, + "total": 1, + "page": 1 + } + ``` + + + +--- + + + + + ### Query + + + Question + + + Answer + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/apps/annotations' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "question": "What is your name?", + "answer": "I am Dify." + }' + ``` + + + + ```json {{ title: 'Response' }} + { + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + } + ``` + + + +--- + + + + + ### Query + + + Annotation ID + + + Question + + + Answer + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request POST '${props.apiBaseUrl}/apps/annotations/{annotation_id}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "question": "What is your name?", + "answer": "I am Dify." + }' + ``` + + + + ```json {{ title: 'Response' }} + { + { + "id": "69d48372-ad81-4c75-9c46-2ce197b4d402", + "question": "What is your name?", + "answer": "I am Dify.", + "hit_count": 0, + "created_at": 1735625869 + } + } + ``` + + + +--- + + + + + ### Query + + + Annotation ID + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request DELETE '${props.apiBaseUrl}/apps/annotations/{annotation_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + {"result": "success"} + ``` + + + +--- + + + + + ### Query + + + Action, can only be 'enable' or 'disable' + + + Specified embedding model provider, must be set up in the system first, corresponding to the provider field(Optional) + + + Specified embedding model, corresponding to the model field(Optional) + + + The similarity threshold for matching annotated replies. Only annotations with scores above this threshold will be recalled. + + + + + The provider and model name of the embedding model can be obtained through the following interface: v1/workspaces/current/models/model-types/text-embedding. For specific instructions, see: Maintain Knowledge Base via API. The Authorization used is the Dataset API Token. + + ```bash {{ title: 'cURL' }} + curl --location --request POST 'https://api.dify.ai/v1/apps/annotation-reply/{action}' \ + --header 'Authorization: Bearer {api_key}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "score_threshold": 0.9, + "embedding_provider_name": "zhipu", + "embedding_model_name": "embedding_3" + }' + ``` + + + + ```json {{ title: 'Response' }} + { + "job_id": "b15c8f68-1cf4-4877-bf21-ed7cf2011802", + "job_status": "waiting" + } + ``` + This interface is executed asynchronously, so it will return a job_id. You can get the final execution result by querying the job status interface. + + + +--- + + + + + ### Query + + + Action, can only be 'enable' or 'disable', must be the same as the action in the initial annotation reply settings interface + + + Job ID, + + + + + + ```bash {{ title: 'cURL' }} + curl --location --request GET '${props.apiBaseUrl}/apps/annotation-reply/{action}/status/{job_id}' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "job_id": "b15c8f68-1cf4-4877-bf21-ed7cf2011802", + "job_status": "waiting", + "error_msg": "" + } + ``` + + +