refactor(api): migrate dataset endpoints to BaseModel (#37957)

Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
chariri 2026-07-09 13:22:30 +09:00 committed by GitHub
parent 9bb3b1fa98
commit 89d5f74a40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 1871 additions and 1403 deletions

View File

@ -30,6 +30,7 @@ from controllers.console.wraps import (
with_current_tenant_id, with_current_tenant_id,
with_current_user, with_current_user,
) )
from core.entities.knowledge_entities import IndexingEstimate
from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError
from core.indexing_runner import IndexingRunner from core.indexing_runner import IndexingRunner
from core.plugin.impl.model_runtime_factory import create_plugin_provider_manager from core.plugin.impl.model_runtime_factory import create_plugin_provider_manager
@ -268,21 +269,10 @@ class ErrorDocsResponse(DocumentStatusListResponse):
total: int total: int
class IndexingEstimatePreviewItemResponse(ResponseModel): class IndexingEstimateResponse(IndexingEstimate):
content: str tokens: int
child_chunks: list[str] | None = None total_price: float | int
summary: str | None = None currency: str
class IndexingEstimateQaPreviewItemResponse(ResponseModel):
question: str
answer: str
class IndexingEstimateResponse(ResponseModel):
total_segments: int
preview: list[IndexingEstimatePreviewItemResponse]
qa_preview: list[IndexingEstimateQaPreviewItemResponse] | None = None
class RetrievalSettingResponse(ResponseModel): class RetrievalSettingResponse(ResponseModel):
@ -647,7 +637,7 @@ class DatasetApi(Resource):
else: else:
data["embedding_available"] = True data["embedding_available"] = True
return data, 200 return dump_response(DatasetDetailWithPartialMembersResponse, data), 200
@console_ns.doc("update_dataset") @console_ns.doc("update_dataset")
@console_ns.doc(description="Update dataset details") @console_ns.doc(description="Update dataset details")
@ -717,7 +707,7 @@ class DatasetApi(Resource):
partial_member_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str, db.session()) partial_member_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str, db.session())
result_data.update({"partial_member_list": partial_member_list}) result_data.update({"partial_member_list": partial_member_list})
return result_data, 200 return dump_response(DatasetDetailWithPartialMembersResponse, result_data), 200
@setup_required @setup_required
@login_required @login_required
@ -760,7 +750,7 @@ class DatasetUseCheckApi(Resource):
dataset_id_str = str(dataset_id) dataset_id_str = str(dataset_id)
dataset_is_using = DatasetService.dataset_use_check(dataset_id_str, db.session()) dataset_is_using = DatasetService.dataset_use_check(dataset_id_str, db.session())
return {"is_using": dataset_is_using}, 200 return UsageCheckResponse(is_using=dataset_is_using).model_dump(mode="json"), 200
@console_ns.route("/datasets/<uuid:dataset_id>/queries") @console_ns.route("/datasets/<uuid:dataset_id>/queries")
@ -901,7 +891,17 @@ class DatasetIndexingEstimateApi(Resource):
except Exception as e: except Exception as e:
raise IndexingEstimateError(str(e)) raise IndexingEstimateError(str(e))
return response.model_dump(), 200 return (
IndexingEstimateResponse(
tokens=0,
total_price=0,
currency="USD",
total_segments=response.total_segments,
preview=response.preview,
qa_preview=response.qa_preview,
).model_dump(mode="json", exclude_none=True),
200,
)
@console_ns.route("/datasets/<uuid:dataset_id>/related-apps") @console_ns.route("/datasets/<uuid:dataset_id>/related-apps")
@ -1018,7 +1018,7 @@ class DatasetApiKeyApi(Resource):
keys = db.session.scalars( keys = db.session.scalars(
select(ApiToken).where(ApiToken.type == self.resource_type, ApiToken.tenant_id == current_tenant_id) select(ApiToken).where(ApiToken.type == self.resource_type, ApiToken.tenant_id == current_tenant_id)
).all() ).all()
return ApiKeyList.model_validate({"data": keys}, from_attributes=True).model_dump(mode="json") return dump_response(ApiKeyList, {"data": keys})
@console_ns.response(200, "API key created successfully", console_ns.models[ApiKeyItem.__name__]) @console_ns.response(200, "API key created successfully", console_ns.models[ApiKeyItem.__name__])
@console_ns.response(400, "Maximum keys exceeded") @console_ns.response(400, "Maximum keys exceeded")
@ -1052,7 +1052,7 @@ class DatasetApiKeyApi(Resource):
api_token.type = self.resource_type api_token.type = self.resource_type
db.session.add(api_token) db.session.add(api_token)
db.session.commit() db.session.commit()
return ApiKeyItem.model_validate(api_token, from_attributes=True).model_dump(mode="json"), 200 return dump_response(ApiKeyItem, api_token), 200
@console_ns.route("/datasets/api-keys/<uuid:api_key_id>") @console_ns.route("/datasets/api-keys/<uuid:api_key_id>")
@ -1107,7 +1107,7 @@ class DatasetEnableApiApi(Resource):
DatasetService.update_dataset_api_status(dataset_id_str, status == "enable", db.session()) DatasetService.update_dataset_api_status(dataset_id_str, status == "enable", db.session())
return {"result": "success"}, 200 return SimpleResultResponse(result="success").model_dump(mode="json"), 200
@console_ns.route("/datasets/api-base-info") @console_ns.route("/datasets/api-base-info")
@ -1120,7 +1120,7 @@ class DatasetApiBaseUrlApi(Resource):
@account_initialization_required @account_initialization_required
def get(self): def get(self):
base = dify_config.SERVICE_API_URL or request.host_url.rstrip("/") base = dify_config.SERVICE_API_URL or request.host_url.rstrip("/")
return {"api_base_url": normalize_api_base_url(base)} return ApiBaseUrlResponse(api_base_url=normalize_api_base_url(base)).model_dump(mode="json")
@console_ns.route("/datasets/retrieval-setting") @console_ns.route("/datasets/retrieval-setting")

View File

@ -10,16 +10,17 @@ from uuid import UUID
import sqlalchemy as sa import sqlalchemy as sa
from flask import request, send_file from flask import request, send_file
from flask_restx import Resource from flask_restx import Resource
from pydantic import BaseModel, Field, RootModel, field_validator from pydantic import BaseModel, Field, JsonValue, field_validator
from sqlalchemy import asc, desc, func, select from sqlalchemy import asc, desc, func, select
from werkzeug.exceptions import Forbidden, NotFound from werkzeug.exceptions import Forbidden, NotFound
import services import services
from controllers.common.controller_schemas import DocumentBatchDownloadZipPayload from controllers.common.controller_schemas import DocumentBatchDownloadZipPayload
from controllers.common.fields import BinaryFileResponse, SimpleResultMessageResponse, SimpleResultResponse, UrlResponse from controllers.common.fields import SimpleResultMessageResponse, SimpleResultResponse, UrlResponse
from controllers.common.schema import register_response_schema_models, register_schema_models from controllers.common.schema import register_response_schema_models, register_schema_models
from controllers.console import console_ns from controllers.console import console_ns
from controllers.console.wraps import RBACPermission, RBACResourceScope, rbac_permission_required from controllers.console.wraps import RBACPermission, RBACResourceScope, rbac_permission_required
from core.entities.knowledge_entities import IndexingEstimate
from core.errors.error import ( from core.errors.error import (
LLMBadRequestError, LLMBadRequestError,
ModelCurrentlyNotSupportError, ModelCurrentlyNotSupportError,
@ -29,6 +30,7 @@ from core.errors.error import (
from core.indexing_runner import IndexingRunner from core.indexing_runner import IndexingRunner
from core.model_manager import ModelManager from core.model_manager import ModelManager
from core.plugin.impl.exc import PluginDaemonClientSideError from core.plugin.impl.exc import PluginDaemonClientSideError
from core.rag.entities import Rule
from core.rag.extractor.entity.datasource_type import DatasourceType from core.rag.extractor.entity.datasource_type import DatasourceType
from core.rag.extractor.entity.extract_setting import ExtractSetting, NotionInfo, WebsiteInfo from core.rag.extractor.entity.extract_setting import ExtractSetting, NotionInfo, WebsiteInfo
from core.rag.index_processor.constant.index_type import IndexTechniqueType from core.rag.index_processor.constant.index_type import IndexTechniqueType
@ -49,7 +51,7 @@ from libs.login import login_required
from libs.pagination import paginate_query from libs.pagination import paginate_query
from models import Account, DatasetProcessRule, Document, DocumentSegment, UploadFile from models import Account, DatasetProcessRule, Document, DocumentSegment, UploadFile
from models.dataset import DocumentPipelineExecutionLog from models.dataset import DocumentPipelineExecutionLog
from models.enums import IndexingStatus, SegmentStatus from models.enums import IndexingStatus, ProcessRuleMode, SegmentStatus
from services.dataset_ref_service import DatasetRefService from services.dataset_ref_service import DatasetRefService
from services.dataset_service import DatasetService, DocumentService from services.dataset_service import DatasetService, DocumentService
from services.entities.knowledge_entities.knowledge_entities import KnowledgeConfig, ProcessRule, RetrievalModel from services.entities.knowledge_entities.knowledge_entities import KnowledgeConfig, ProcessRule, RetrievalModel
@ -148,8 +150,91 @@ class DocumentWithSegmentsListResponse(ResponseModel):
page: int page: int
class OpaqueObjectResponse(RootModel[dict[str, Any]]): class IndexingEstimateResponse(IndexingEstimate):
root: dict[str, Any] tokens: int
total_price: float | int
currency: str
class DocumentDetailResponse(ResponseModel):
id: str
position: int | None = None
data_source_type: str | None = None
data_source_info: Any = None
data_source_detail_dict: Any = None
dataset_process_rule_id: str | None = None
dataset_process_rule: Any = None
document_process_rule: Any = None
name: str | None = None
created_from: str | None = None
created_by: str | None = None
created_at: int | None = None
tokens: int | None = None
indexing_status: str | None = None
completed_at: int | None = None
updated_at: int | None = None
indexing_latency: float | None = None
error: str | None = None
enabled: bool | None = None
disabled_at: int | None = None
disabled_by: str | None = None
archived: bool | None = None
doc_type: str | None = None
doc_metadata: list[DocumentMetadataResponse] | None = None
segment_count: int | None = None
average_segment_length: float | None = None
hit_count: int | None = None
display_status: str | None = None
doc_form: str | None = None
doc_language: str | None = None
need_summary: bool | None = None
@field_validator("data_source_type", "indexing_status", "display_status", "doc_form", mode="before")
@classmethod
def _normalize_enum_fields(cls, value: Any) -> Any:
return normalize_enum(value)
class SummaryStatusResponse(ResponseModel):
completed: int = 0
generating: int = 0
error: int = 0
not_started: int = 0
timeout: int = 0
class SummaryEntryResponse(ResponseModel):
segment_id: str
segment_position: int
status: str
summary_preview: str | None = None
error: str | None = None
created_at: int | None = None
updated_at: int | None = None
@field_validator("status", mode="before")
@classmethod
def _normalize_status(cls, value: Any) -> Any:
return normalize_enum(value)
class DocumentSummaryStatusResponse(ResponseModel):
total_segments: int
summary_status: SummaryStatusResponse
summaries: list[SummaryEntryResponse]
class ProcessRuleResponse(ResponseModel):
mode: ProcessRuleMode
rules: Rule | None = None
limits: dict[str, Any]
class DocumentPipelineExecutionLogResponse(ResponseModel):
datasource_info: JsonValue | None = None
datasource_type: str | None = None
input_data: JsonValue | None = None
datasource_node_id: str | None = None
register_schema_models( register_schema_models(
@ -165,7 +250,6 @@ register_schema_models(
) )
register_response_schema_models( register_response_schema_models(
console_ns, console_ns,
BinaryFileResponse,
SimpleResultMessageResponse, SimpleResultMessageResponse,
SimpleResultResponse, SimpleResultResponse,
UrlResponse, UrlResponse,
@ -175,7 +259,11 @@ register_response_schema_models(
DocumentWithSegmentsResponse, DocumentWithSegmentsResponse,
DatasetAndDocumentResponse, DatasetAndDocumentResponse,
DocumentWithSegmentsListResponse, DocumentWithSegmentsListResponse,
OpaqueObjectResponse, IndexingEstimateResponse,
DocumentDetailResponse,
DocumentSummaryStatusResponse,
ProcessRuleResponse,
DocumentPipelineExecutionLogResponse,
) )
@ -225,7 +313,7 @@ class GetProcessRuleApi(Resource):
@console_ns.doc("get_process_rule") @console_ns.doc("get_process_rule")
@console_ns.doc(description="Get dataset document processing rules") @console_ns.doc(description="Get dataset document processing rules")
@console_ns.doc(params={"document_id": "Document ID (optional)"}) @console_ns.doc(params={"document_id": "Document ID (optional)"})
@console_ns.response(200, "Process rules retrieved successfully", console_ns.models[OpaqueObjectResponse.__name__]) @console_ns.response(200, "Process rules retrieved successfully", console_ns.models[ProcessRuleResponse.__name__])
@setup_required @setup_required
@login_required @login_required
@account_initialization_required @account_initialization_required
@ -264,7 +352,7 @@ class GetProcessRuleApi(Resource):
mode = dataset_process_rule.mode mode = dataset_process_rule.mode
rules = dataset_process_rule.rules_dict rules = dataset_process_rule.rules_dict
return {"mode": mode, "rules": rules, "limits": limits} return dump_response(ProcessRuleResponse, {"mode": mode, "rules": rules, "limits": limits})
@console_ns.route("/datasets/<uuid:dataset_id>/documents") @console_ns.route("/datasets/<uuid:dataset_id>/documents")
@ -491,7 +579,7 @@ class DatasetInitApi(Resource):
@console_ns.doc(description="Initialize dataset with documents") @console_ns.doc(description="Initialize dataset with documents")
@console_ns.expect(console_ns.models[KnowledgeConfig.__name__]) @console_ns.expect(console_ns.models[KnowledgeConfig.__name__])
@console_ns.response( @console_ns.response(
201, "Dataset initialized successfully", console_ns.models[DatasetAndDocumentResponse.__name__] 200, "Dataset initialized successfully", console_ns.models[DatasetAndDocumentResponse.__name__]
) )
@console_ns.response(400, "Invalid request parameters") @console_ns.response(400, "Invalid request parameters")
@setup_required @setup_required
@ -557,7 +645,7 @@ class DocumentIndexingEstimateApi(DocumentResource):
@console_ns.response( @console_ns.response(
200, 200,
"Indexing estimate calculated successfully", "Indexing estimate calculated successfully",
console_ns.models[OpaqueObjectResponse.__name__], console_ns.models[IndexingEstimateResponse.__name__],
) )
@console_ns.response(404, "Document not found") @console_ns.response(404, "Document not found")
@console_ns.response(400, "Document already finished") @console_ns.response(400, "Document already finished")
@ -578,8 +666,6 @@ class DocumentIndexingEstimateApi(DocumentResource):
data_process_rule = document.dataset_process_rule data_process_rule = document.dataset_process_rule
data_process_rule_dict = data_process_rule.to_dict() if data_process_rule else {} data_process_rule_dict = data_process_rule.to_dict() if data_process_rule else {}
response = {"tokens": 0, "total_price": 0, "currency": "USD", "total_segments": 0, "preview": []}
if document.data_source_type == "upload_file": if document.data_source_type == "upload_file":
data_source_info = document.data_source_info_dict data_source_info = document.data_source_info_dict
if data_source_info and "upload_file_id" in data_source_info: if data_source_info and "upload_file_id" in data_source_info:
@ -610,7 +696,18 @@ class DocumentIndexingEstimateApi(DocumentResource):
"English", "English",
dataset_id_str, dataset_id_str,
) )
return estimate_response.model_dump(), 200 return (
# TODO: why using zero here? the same for the below endpoint
IndexingEstimateResponse(
tokens=0,
total_price=0,
currency="USD",
total_segments=estimate_response.total_segments,
preview=estimate_response.preview,
qa_preview=estimate_response.qa_preview,
).model_dump(mode="json", exclude_none=True),
200,
)
except LLMBadRequestError: except LLMBadRequestError:
raise ProviderNotInitializeError( raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider " "No Embedding Model available. Please configure a valid provider "
@ -623,15 +720,24 @@ class DocumentIndexingEstimateApi(DocumentResource):
except Exception as e: except Exception as e:
raise IndexingEstimateError(str(e)) raise IndexingEstimateError(str(e))
return response, 200 return (
IndexingEstimateResponse(
tokens=0,
total_price=0,
currency="USD",
total_segments=0,
preview=[],
).model_dump(mode="json", exclude_none=True),
200,
)
@console_ns.route("/datasets/<uuid:dataset_id>/batch/<string:batch>/indexing-estimate") @console_ns.route("/datasets/<uuid:dataset_id>/batch/<string:batch>/indexing-estimate")
class DocumentBatchIndexingEstimateApi(DocumentResource): class DocumentBatchIndexingEstimateApi(DocumentResource):
@console_ns.response( @console_ns.response(
200, 200,
"Batch indexing estimate calculated successfully", "Indexing estimate calculated successfully",
console_ns.models[OpaqueObjectResponse.__name__], console_ns.models[IndexingEstimateResponse.__name__],
) )
@setup_required @setup_required
@login_required @login_required
@ -643,7 +749,16 @@ class DocumentBatchIndexingEstimateApi(DocumentResource):
dataset_id_str = str(dataset_id) dataset_id_str = str(dataset_id)
documents = self.get_batch_documents(dataset_id_str, batch, current_user) documents = self.get_batch_documents(dataset_id_str, batch, current_user)
if not documents: if not documents:
return {"tokens": 0, "total_price": 0, "currency": "USD", "total_segments": 0, "preview": []}, 200 return (
IndexingEstimateResponse(
tokens=0,
total_price=0,
currency="USD",
total_segments=0,
preview=[],
).model_dump(mode="json", exclude_none=True),
200,
)
data_process_rule = documents[0].dataset_process_rule data_process_rule = documents[0].dataset_process_rule
data_process_rule_dict = data_process_rule.to_dict() if data_process_rule else {} data_process_rule_dict = data_process_rule.to_dict() if data_process_rule else {}
extract_settings = [] extract_settings = []
@ -717,7 +832,17 @@ class DocumentBatchIndexingEstimateApi(DocumentResource):
"English", "English",
dataset_id_str, dataset_id_str,
) )
return response.model_dump(), 200 return (
IndexingEstimateResponse(
tokens=0,
total_price=0,
currency="USD",
total_segments=response.total_segments,
preview=response.preview,
qa_preview=response.qa_preview,
).model_dump(mode="json", exclude_none=True),
200,
)
except LLMBadRequestError: except LLMBadRequestError:
raise ProviderNotInitializeError( raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider." "No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
@ -854,7 +979,7 @@ class DocumentApi(DocumentResource):
"metadata": "Metadata inclusion (all/only/without)", "metadata": "Metadata inclusion (all/only/without)",
} }
) )
@console_ns.response(200, "Document retrieved successfully", console_ns.models[OpaqueObjectResponse.__name__]) @console_ns.response(200, "Document retrieved successfully", console_ns.models[DocumentDetailResponse.__name__])
@console_ns.response(404, "Document not found") @console_ns.response(404, "Document not found")
@setup_required @setup_required
@login_required @login_required
@ -871,46 +996,21 @@ class DocumentApi(DocumentResource):
if metadata not in self.METADATA_CHOICES: if metadata not in self.METADATA_CHOICES:
raise InvalidMetadataError(f"Invalid metadata value: {metadata}") raise InvalidMetadataError(f"Invalid metadata value: {metadata}")
metadata_fields = {"doc_type", "doc_metadata"}
if metadata == "only": if metadata == "only":
response = {"id": document.id, "doc_type": document.doc_type, "doc_metadata": document.doc_metadata_details} response = DocumentDetailResponse.model_validate(
elif metadata == "without": {
dataset_process_rules = DatasetService.get_process_rules(dataset_id_str, db.session()) "id": document.id,
document_process_rules = document.dataset_process_rule.to_dict() if document.dataset_process_rule else {} "doc_type": document.doc_type,
response = { "doc_metadata": document.doc_metadata_details,
"id": document.id, }
"position": document.position, )
"data_source_type": document.data_source_type, return response.model_dump(mode="json", include={"id", *metadata_fields}, exclude_unset=True), 200
"data_source_info": document.data_source_info_dict,
"data_source_detail_dict": document.data_source_detail_dict, dataset_process_rules = DatasetService.get_process_rules(dataset_id_str, db.session())
"dataset_process_rule_id": document.dataset_process_rule_id, document_process_rules = document.dataset_process_rule.to_dict() if document.dataset_process_rule else {}
"dataset_process_rule": dataset_process_rules, response = DocumentDetailResponse.model_validate(
"document_process_rule": document_process_rules, {
"name": document.name,
"created_from": document.created_from,
"created_by": document.created_by,
"created_at": int(document.created_at.timestamp()),
"tokens": document.tokens,
"indexing_status": document.indexing_status,
"completed_at": int(document.completed_at.timestamp()) if document.completed_at else None,
"updated_at": int(document.updated_at.timestamp()) if document.updated_at else None,
"indexing_latency": document.indexing_latency,
"error": document.error,
"enabled": document.enabled,
"disabled_at": int(document.disabled_at.timestamp()) if document.disabled_at else None,
"disabled_by": document.disabled_by,
"archived": document.archived,
"segment_count": document.segment_count,
"average_segment_length": document.average_segment_length,
"hit_count": document.hit_count,
"display_status": document.display_status,
"doc_form": document.doc_form,
"doc_language": document.doc_language,
"need_summary": document.need_summary if document.need_summary is not None else False,
}
else:
dataset_process_rules = DatasetService.get_process_rules(dataset_id_str, db.session())
document_process_rules = document.dataset_process_rule.to_dict() if document.dataset_process_rule else {}
response = {
"id": document.id, "id": document.id,
"position": document.position, "position": document.position,
"data_source_type": document.data_source_type, "data_source_type": document.data_source_type,
@ -943,8 +1043,9 @@ class DocumentApi(DocumentResource):
"doc_language": document.doc_language, "doc_language": document.doc_language,
"need_summary": document.need_summary if document.need_summary is not None else False, "need_summary": document.need_summary if document.need_summary is not None else False,
} }
)
return response, 200 exclude = metadata_fields if metadata == "without" else None
return response.model_dump(mode="json", exclude=exclude, exclude_unset=True), 200
@setup_required @setup_required
@login_required @login_required
@ -990,7 +1091,9 @@ class DocumentDownloadApi(DocumentResource):
def get(self, current_tenant_id: str, current_user: Account, dataset_id: UUID, document_id: UUID) -> dict[str, Any]: def get(self, current_tenant_id: str, current_user: Account, dataset_id: UUID, document_id: UUID) -> dict[str, Any]:
# Reuse the shared permission/tenant checks implemented in DocumentResource. # Reuse the shared permission/tenant checks implemented in DocumentResource.
document = self.get_document(str(dataset_id), str(document_id), current_user, current_tenant_id) document = self.get_document(str(dataset_id), str(document_id), current_user, current_tenant_id)
return {"url": DocumentService.get_document_download_url(document, db.session())} return UrlResponse(url=DocumentService.get_document_download_url(document, db.session())).model_dump(
mode="json"
)
@console_ns.route("/datasets/<uuid:dataset_id>/documents/download-zip") @console_ns.route("/datasets/<uuid:dataset_id>/documents/download-zip")
@ -999,7 +1102,7 @@ class DocumentBatchDownloadZipApi(DocumentResource):
@console_ns.doc("download_dataset_documents_as_zip") @console_ns.doc("download_dataset_documents_as_zip")
@console_ns.doc(description="Download selected dataset documents as a single ZIP archive (upload-file only)") @console_ns.doc(description="Download selected dataset documents as a single ZIP archive (upload-file only)")
@console_ns.response(200, "ZIP archive generated successfully", console_ns.models[BinaryFileResponse.__name__]) @console_ns.response(200, "ZIP archive downloaded successfully")
@setup_required @setup_required
@login_required @login_required
@account_initialization_required @account_initialization_required
@ -1034,6 +1137,7 @@ class DocumentBatchDownloadZipApi(DocumentResource):
) )
cleanup = stack.pop_all() cleanup = stack.pop_all()
response.call_on_close(cleanup.close) response.call_on_close(cleanup.close)
# response-contract:ignore binary ZIP download response
return response return response
@ -1093,7 +1197,7 @@ class DocumentProcessingApi(DocumentResource):
document.is_paused = False document.is_paused = False
db.session.commit() db.session.commit()
return {"result": "success"}, 200 return SimpleResultResponse(result="success").model_dump(mode="json"), 200
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/metadata") @console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/metadata")
@ -1152,7 +1256,9 @@ class DocumentMetadataApi(DocumentResource):
document.updated_at = naive_utc_now() document.updated_at = naive_utc_now()
db.session.commit() db.session.commit()
return {"result": "success", "message": "Document metadata updated."}, 200 return SimpleResultMessageResponse(result="success", message="Document metadata updated.").model_dump(
mode="json"
), 200
@console_ns.route("/datasets/<uuid:dataset_id>/documents/status/<string:action>/batch") @console_ns.route("/datasets/<uuid:dataset_id>/documents/status/<string:action>/batch")
@ -1194,7 +1300,7 @@ class DocumentStatusApi(DocumentResource):
except NotFound as e: except NotFound as e:
raise NotFound(str(e)) raise NotFound(str(e))
return {"result": "success"}, 200 return SimpleResultResponse(result="success").model_dump(mode="json"), 200
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/processing/pause") @console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/processing/pause")
@ -1321,7 +1427,7 @@ class DocumentRenameApi(DocumentResource):
# The role of the current user in the ta table must be admin, owner, editor, or dataset_operator # The role of the current user in the ta table must be admin, owner, editor, or dataset_operator
if not current_user.is_dataset_editor: if not current_user.is_dataset_editor:
raise Forbidden() raise Forbidden()
dataset = DatasetService.get_dataset(dataset_id, db.session()) dataset = DatasetService.get_dataset(str(dataset_id), db.session())
if not dataset: if not dataset:
raise NotFound("Dataset not found.") raise NotFound("Dataset not found.")
DatasetService.check_dataset_operator_permission(current_user, dataset, session=db.session()) DatasetService.check_dataset_operator_permission(current_user, dataset, session=db.session())
@ -1363,15 +1469,15 @@ class WebsiteDocumentSyncApi(DocumentResource):
# sync document # sync document
DocumentService.sync_website_document(dataset_id_str, document, db.session()) DocumentService.sync_website_document(dataset_id_str, document, db.session())
return {"result": "success"}, 200 return SimpleResultResponse(result="success").model_dump(mode="json"), 200
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/pipeline-execution-log") @console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/pipeline-execution-log")
class DocumentPipelineExecutionLogApi(DocumentResource): class DocumentPipelineExecutionLogApi(DocumentResource):
@console_ns.response( @console_ns.response(
200, 200,
"Document pipeline execution log retrieved successfully", "Pipeline execution log retrieved successfully",
console_ns.models[OpaqueObjectResponse.__name__], console_ns.models[DocumentPipelineExecutionLogResponse.__name__],
) )
@setup_required @setup_required
@login_required @login_required
@ -1394,18 +1500,16 @@ class DocumentPipelineExecutionLogApi(DocumentResource):
.limit(1) .limit(1)
) )
if not log: if not log:
return { return DocumentPipelineExecutionLogResponse().model_dump(mode="json"), 200
"datasource_info": None, return dump_response(
"datasource_type": None, DocumentPipelineExecutionLogResponse,
"input_data": None, {
"datasource_node_id": None, "datasource_info": json.loads(log.datasource_info),
}, 200 "datasource_type": log.datasource_type,
return { "input_data": log.input_data,
"datasource_info": json.loads(log.datasource_info), "datasource_node_id": log.datasource_node_id,
"datasource_type": log.datasource_type, },
"input_data": log.input_data, ), 200
"datasource_node_id": log.datasource_node_id,
}, 200
@console_ns.route("/datasets/<uuid:dataset_id>/documents/generate-summary") @console_ns.route("/datasets/<uuid:dataset_id>/documents/generate-summary")
@ -1508,7 +1612,7 @@ class DocumentGenerateSummaryApi(Resource):
dataset_id_str, dataset_id_str,
) )
return {"result": "success"}, 200 return SimpleResultResponse(result="success").model_dump(mode="json"), 200
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/summary-status") @console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/summary-status")
@ -1516,7 +1620,11 @@ class DocumentSummaryStatusApi(DocumentResource):
@console_ns.doc("get_document_summary_status") @console_ns.doc("get_document_summary_status")
@console_ns.doc(description="Get summary index generation status for a document") @console_ns.doc(description="Get summary index generation status for a document")
@console_ns.doc(params={"dataset_id": "Dataset ID", "document_id": "Document ID"}) @console_ns.doc(params={"dataset_id": "Dataset ID", "document_id": "Document ID"})
@console_ns.response(200, "Summary status retrieved successfully", console_ns.models[OpaqueObjectResponse.__name__]) @console_ns.response(
200,
"Summary status retrieved successfully",
console_ns.models[DocumentSummaryStatusResponse.__name__],
)
@console_ns.response(404, "Document not found") @console_ns.response(404, "Document not found")
@setup_required @setup_required
@login_required @login_required
@ -1534,6 +1642,7 @@ class DocumentSummaryStatusApi(DocumentResource):
- generating: Number of summaries being generated - generating: Number of summaries being generated
- error: Number of summaries with errors - error: Number of summaries with errors
- not_started: Number of segments without summary records - not_started: Number of segments without summary records
- timeout: Number of summaries that timed out
- summaries: List of summary records with status and content preview - summaries: List of summary records with status and content preview
""" """
dataset_id_str = str(dataset_id) dataset_id_str = str(dataset_id)
@ -1559,4 +1668,4 @@ class DocumentSummaryStatusApi(DocumentResource):
session=db.session(), session=db.session(),
) )
return result, 200 return dump_response(DocumentSummaryStatusResponse, result), 200

View File

@ -391,7 +391,7 @@ class DatasetDocumentSegmentApi(Resource):
SegmentService.update_segments_status(segment_ids, action, dataset, document, db.session()) SegmentService.update_segments_status(segment_ids, action, dataset, document, db.session())
except Exception as e: except Exception as e:
raise InvalidActionError(str(e)) raise InvalidActionError(str(e))
return dump_response(SimpleResultResponse, {"result": "success"}), 200 return SimpleResultResponse(result="success").model_dump(mode="json"), 200
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/segment") @console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/segment")

View File

@ -1,20 +1,16 @@
from datetime import datetime
from typing import Any from typing import Any
from uuid import UUID from uuid import UUID
from flask import request from flask import request
from flask_restx import Resource, fields, marshal from flask_restx import Resource
from pydantic import BaseModel, Field, RootModel from pydantic import AliasChoices, BaseModel, Field, field_validator
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden, InternalServerError, NotFound from werkzeug.exceptions import Forbidden, InternalServerError, NotFound
import services import services
from controllers.common.fields import UsageCountResponse from controllers.common.fields import UsageCountResponse
from controllers.common.schema import ( from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
get_or_create_model,
query_params_from_model,
register_response_schema_models,
register_schema_models,
)
from controllers.console import console_ns from controllers.console import console_ns
from controllers.console.app.wraps import with_session from controllers.console.app.wraps import with_session
from controllers.console.datasets.error import DatasetNameDuplicateError from controllers.console.datasets.error import DatasetNameDuplicateError
@ -28,21 +24,9 @@ from controllers.console.wraps import (
with_current_tenant_id, with_current_tenant_id,
with_current_user, with_current_user,
) )
from extensions.ext_database import db
from fields.base import ResponseModel from fields.base import ResponseModel
from fields.dataset_fields import ( from fields.dataset_fields import DatasetDetailResponse
dataset_detail_fields, from libs.helper import dump_response
dataset_retrieval_model_fields,
doc_metadata_fields,
external_knowledge_info_fields,
external_retrieval_model_fields,
icon_info_fields,
keyword_setting_fields,
reranking_model_fields,
tag_fields,
vector_setting_fields,
weighted_score_fields,
)
from libs.login import login_required from libs.login import login_required
from models import Account from models import Account
from services.dataset_service import DatasetService from services.dataset_service import DatasetService
@ -51,50 +35,10 @@ from services.external_knowledge_service import ExternalDatasetService
from services.hit_testing_service import HitTestingService from services.hit_testing_service import HitTestingService
from services.knowledge_service import BedrockRetrievalSetting, ExternalDatasetTestService from services.knowledge_service import BedrockRetrievalSetting, ExternalDatasetTestService
register_response_schema_models(console_ns, UsageCountResponse)
def _build_dataset_detail_model():
keyword_setting_model = get_or_create_model("DatasetKeywordSetting", keyword_setting_fields)
vector_setting_model = get_or_create_model("DatasetVectorSetting", vector_setting_fields)
weighted_score_fields_copy = weighted_score_fields.copy()
weighted_score_fields_copy["keyword_setting"] = fields.Nested(keyword_setting_model)
weighted_score_fields_copy["vector_setting"] = fields.Nested(vector_setting_model)
weighted_score_model = get_or_create_model("DatasetWeightedScore", weighted_score_fields_copy)
reranking_model = get_or_create_model("DatasetRerankingModel", reranking_model_fields)
dataset_retrieval_model_fields_copy = dataset_retrieval_model_fields.copy()
dataset_retrieval_model_fields_copy["reranking_model"] = fields.Nested(reranking_model)
dataset_retrieval_model_fields_copy["weights"] = fields.Nested(weighted_score_model, allow_null=True)
dataset_retrieval_model = get_or_create_model("DatasetRetrievalModel", dataset_retrieval_model_fields_copy)
tag_model = get_or_create_model("Tag", tag_fields)
doc_metadata_model = get_or_create_model("DatasetDocMetadata", doc_metadata_fields)
external_knowledge_info_model = get_or_create_model("ExternalKnowledgeInfo", external_knowledge_info_fields)
external_retrieval_model = get_or_create_model("ExternalRetrievalModel", external_retrieval_model_fields)
icon_info_model = get_or_create_model("DatasetIconInfo", icon_info_fields)
dataset_detail_fields_copy = dataset_detail_fields.copy()
dataset_detail_fields_copy["retrieval_model_dict"] = fields.Nested(dataset_retrieval_model)
dataset_detail_fields_copy["tags"] = fields.List(fields.Nested(tag_model))
dataset_detail_fields_copy["external_knowledge_info"] = fields.Nested(external_knowledge_info_model)
dataset_detail_fields_copy["external_retrieval_model"] = fields.Nested(external_retrieval_model, allow_null=True)
dataset_detail_fields_copy["doc_metadata"] = fields.List(fields.Nested(doc_metadata_model))
dataset_detail_fields_copy["icon_info"] = fields.Nested(icon_info_model)
return get_or_create_model("DatasetDetail", dataset_detail_fields_copy)
try:
dataset_detail_model = console_ns.models["DatasetDetail"]
except KeyError:
dataset_detail_model = _build_dataset_detail_model()
class ExternalKnowledgeApiPayload(BaseModel): class ExternalKnowledgeApiPayload(BaseModel):
name: str = Field(..., min_length=1, max_length=40) name: str = Field(..., min_length=1, max_length=40)
settings: dict[str, object] settings: dict[str, Any]
class ExternalDatasetCreatePayload(BaseModel): class ExternalDatasetCreatePayload(BaseModel):
@ -102,15 +46,13 @@ class ExternalDatasetCreatePayload(BaseModel):
external_knowledge_id: str external_knowledge_id: str
name: str = Field(..., min_length=1, max_length=100) name: str = Field(..., min_length=1, max_length=100)
description: str | None = Field(None, max_length=400) description: str | None = Field(None, max_length=400)
external_retrieval_model: dict[str, object] | None = Field(default=None) external_retrieval_model: dict[str, Any] | None = None
class ExternalHitTestingPayload(BaseModel): class ExternalHitTestingPayload(BaseModel):
query: str query: str
external_retrieval_model: dict[str, object] | None = Field(default=None) external_retrieval_model: dict[str, Any] | None = None
metadata_filtering_conditions: dict[str, object] | None = Field( metadata_filtering_conditions: dict[str, Any] | None = None
default=None,
)
class BedrockRetrievalPayload(BaseModel): class BedrockRetrievalPayload(BaseModel):
@ -125,7 +67,7 @@ class ExternalApiTemplateListQuery(BaseModel):
keyword: str | None = Field(default=None, description="Search keyword") keyword: str | None = Field(default=None, description="Search keyword")
class ExternalKnowledgeDatasetBindingResponse(ResponseModel): class ExternalKnowledgeApiBindingResponse(ResponseModel):
id: str id: str
name: str name: str
@ -135,22 +77,52 @@ class ExternalKnowledgeApiResponse(ResponseModel):
tenant_id: str tenant_id: str
name: str name: str
description: str description: str
settings: dict[str, Any] | None = Field(default=None) settings: dict[str, Any] | None = Field(validation_alias=AliasChoices("settings_dict", "settings"))
dataset_bindings: list[ExternalKnowledgeDatasetBindingResponse] = Field(default_factory=list) dataset_bindings: list[ExternalKnowledgeApiBindingResponse]
created_by: str created_by: str
created_at: str created_at: str
@field_validator("created_at", mode="before")
@classmethod
def _normalize_created_at(cls, value: datetime | str) -> str:
if isinstance(value, datetime):
return value.isoformat()
return value
class ExternalKnowledgeApiListResponse(ResponseModel): class ExternalKnowledgeApiListResponse(ResponseModel):
data: list[ExternalKnowledgeApiResponse] data: list[ExternalKnowledgeApiResponse]
has_more: bool has_more: bool
limit: int limit: int
total: int total: int | None
page: int page: int
class ExternalRetrievalTestResponse(RootModel[dict[str, Any] | list[dict[str, Any]]]): class ExternalHitTestingQueryResponse(ResponseModel):
root: dict[str, Any] | list[dict[str, Any]] content: str
class ExternalHitTestingRecordResponse(ResponseModel):
content: str | None = None
title: str | None = None
score: float | None = None
metadata: dict[str, Any] | None = None
class ExternalHitTestingResponse(ResponseModel):
query: ExternalHitTestingQueryResponse
records: list[ExternalHitTestingRecordResponse]
class BedrockRetrievalRecordResponse(ResponseModel):
metadata: dict[str, Any] | None = None
score: float
title: str | None = None
content: str | None = None
class BedrockRetrievalResponse(ResponseModel):
records: list[BedrockRetrievalRecordResponse]
register_schema_models( register_schema_models(
@ -163,9 +135,16 @@ register_schema_models(
) )
register_response_schema_models( register_response_schema_models(
console_ns, console_ns,
UsageCountResponse,
DatasetDetailResponse,
ExternalKnowledgeApiBindingResponse,
ExternalKnowledgeApiResponse, ExternalKnowledgeApiResponse,
ExternalKnowledgeApiListResponse, ExternalKnowledgeApiListResponse,
ExternalRetrievalTestResponse, ExternalHitTestingQueryResponse,
ExternalHitTestingRecordResponse,
ExternalHitTestingResponse,
BedrockRetrievalRecordResponse,
BedrockRetrievalResponse,
) )
@ -189,24 +168,26 @@ class ExternalApiTemplateListApi(Resource):
external_knowledge_apis, total = ExternalDatasetService.get_external_knowledge_apis( external_knowledge_apis, total = ExternalDatasetService.get_external_knowledge_apis(
query.page, query.limit, current_tenant_id, query.keyword query.page, query.limit, current_tenant_id, query.keyword
) )
response = { return ExternalKnowledgeApiListResponse(
"data": [item.to_dict() for item in external_knowledge_apis], data=[ExternalKnowledgeApiResponse.model_validate(item) for item in external_knowledge_apis],
"has_more": len(external_knowledge_apis) == query.limit, has_more=len(external_knowledge_apis) == query.limit,
"limit": query.limit, limit=query.limit,
"total": total, total=total,
"page": query.page, page=query.page,
} ).model_dump(mode="json"), 200
return response, 200
@setup_required @console_ns.doc("create_external_api_template")
@login_required @console_ns.doc(description="Create external knowledge API template")
@account_initialization_required
@console_ns.expect(console_ns.models[ExternalKnowledgeApiPayload.__name__]) @console_ns.expect(console_ns.models[ExternalKnowledgeApiPayload.__name__])
@console_ns.response( @console_ns.response(
201, 201,
"External API template created successfully", "External API template created successfully",
console_ns.models[ExternalKnowledgeApiResponse.__name__], console_ns.models[ExternalKnowledgeApiResponse.__name__],
) )
@console_ns.response(403, "Permission denied")
@setup_required
@login_required
@account_initialization_required
@with_current_user @with_current_user
@with_current_tenant_id @with_current_tenant_id
@with_session @with_session
@ -229,7 +210,7 @@ class ExternalApiTemplateListApi(Resource):
except services.errors.dataset.DatasetNameDuplicateError: except services.errors.dataset.DatasetNameDuplicateError:
raise DatasetNameDuplicateError() raise DatasetNameDuplicateError()
return external_knowledge_api.to_dict(), 201 return dump_response(ExternalKnowledgeApiResponse, external_knowledge_api), 201
@console_ns.route("/datasets/external-knowledge-api/<uuid:external_knowledge_api_id>") @console_ns.route("/datasets/external-knowledge-api/<uuid:external_knowledge_api_id>")
@ -256,17 +237,21 @@ class ExternalApiTemplateApi(Resource):
if external_knowledge_api is None: if external_knowledge_api is None:
raise NotFound("API template not found.") raise NotFound("API template not found.")
return external_knowledge_api.to_dict(), 200 return dump_response(ExternalKnowledgeApiResponse, external_knowledge_api), 200
@console_ns.doc("update_external_api_template")
@console_ns.doc(description="Update external knowledge API template")
@console_ns.doc(params={"external_knowledge_api_id": "External knowledge API ID"})
@console_ns.expect(console_ns.models[ExternalKnowledgeApiPayload.__name__])
@console_ns.response( @console_ns.response(
200, 200,
"External API template updated successfully", "External API template updated successfully",
console_ns.models[ExternalKnowledgeApiResponse.__name__], console_ns.models[ExternalKnowledgeApiResponse.__name__],
) )
@console_ns.response(404, "Template not found")
@setup_required @setup_required
@login_required @login_required
@account_initialization_required @account_initialization_required
@console_ns.expect(console_ns.models[ExternalKnowledgeApiPayload.__name__])
@with_current_user @with_current_user
@with_current_tenant_id @with_current_tenant_id
@with_session @with_session
@ -284,7 +269,7 @@ class ExternalApiTemplateApi(Resource):
session=session, session=session,
) )
return external_knowledge_api.to_dict(), 200 return dump_response(ExternalKnowledgeApiResponse, external_knowledge_api), 200
@setup_required @setup_required
@login_required @login_required
@ -322,7 +307,7 @@ class ExternalApiUseCheckApi(Resource):
external_knowledge_api_is_using, count = ExternalDatasetService.external_knowledge_api_use_check( external_knowledge_api_is_using, count = ExternalDatasetService.external_knowledge_api_use_check(
external_knowledge_api_id_str, current_tenant_id, session=session external_knowledge_api_id_str, current_tenant_id, session=session
) )
return {"is_using": external_knowledge_api_is_using, "count": count}, 200 return UsageCountResponse(is_using=external_knowledge_api_is_using, count=count).model_dump(mode="json"), 200
@console_ns.route("/datasets/external") @console_ns.route("/datasets/external")
@ -330,7 +315,9 @@ class ExternalDatasetCreateApi(Resource):
@console_ns.doc("create_external_dataset") @console_ns.doc("create_external_dataset")
@console_ns.doc(description="Create external knowledge dataset") @console_ns.doc(description="Create external knowledge dataset")
@console_ns.expect(console_ns.models[ExternalDatasetCreatePayload.__name__]) @console_ns.expect(console_ns.models[ExternalDatasetCreatePayload.__name__])
@console_ns.response(201, "External dataset created successfully", dataset_detail_model) @console_ns.response(
201, "External dataset created successfully", console_ns.models[DatasetDetailResponse.__name__]
)
@console_ns.response(400, "Invalid parameters") @console_ns.response(400, "Invalid parameters")
@console_ns.response(403, "Permission denied") @console_ns.response(403, "Permission denied")
@setup_required @setup_required
@ -360,17 +347,16 @@ class ExternalDatasetCreateApi(Resource):
except services.errors.dataset.DatasetNameDuplicateError: except services.errors.dataset.DatasetNameDuplicateError:
raise DatasetNameDuplicateError() raise DatasetNameDuplicateError()
item = marshal(dataset, dataset_detail_fields) dataset_id_str = str(dataset.id)
dataset_id_str = item["id"]
permission_keys_map = enterprise_rbac_service.RBACService.DatasetPermissions.batch_get( permission_keys_map = enterprise_rbac_service.RBACService.DatasetPermissions.batch_get(
str(current_tenant_id), str(current_tenant_id),
current_user.id, current_user.id,
[dataset_id_str], [dataset_id_str],
session=session, session=session,
) )
item["permission_keys"] = permission_keys_map.get(dataset_id_str, []) data = DatasetDetailResponse.model_validate(dataset).model_dump(mode="json")
data["permission_keys"] = permission_keys_map.get(dataset_id_str, [])
return item, 201 return data, 201
@console_ns.route("/datasets/<uuid:dataset_id>/external-hit-testing") @console_ns.route("/datasets/<uuid:dataset_id>/external-hit-testing")
@ -382,7 +368,7 @@ class ExternalKnowledgeHitTestingApi(Resource):
@console_ns.response( @console_ns.response(
200, 200,
"External hit testing completed successfully", "External hit testing completed successfully",
console_ns.models[ExternalRetrievalTestResponse.__name__], console_ns.models[ExternalHitTestingResponse.__name__],
) )
@console_ns.response(404, "Dataset not found") @console_ns.response(404, "Dataset not found")
@console_ns.response(400, "Invalid parameters") @console_ns.response(400, "Invalid parameters")
@ -394,12 +380,12 @@ class ExternalKnowledgeHitTestingApi(Resource):
@with_session @with_session
def post(self, session: Session, current_user: Account, dataset_id: UUID): def post(self, session: Session, current_user: Account, dataset_id: UUID):
dataset_id_str = str(dataset_id) dataset_id_str = str(dataset_id)
dataset = DatasetService.get_dataset(dataset_id_str, db.session()) dataset = DatasetService.get_dataset(dataset_id_str, session)
if dataset is None: if dataset is None:
raise NotFound("Dataset not found.") raise NotFound("Dataset not found.")
try: try:
DatasetService.check_dataset_permission(dataset, current_user, db.session()) DatasetService.check_dataset_permission(dataset, current_user, session)
except services.errors.account.NoPermissionError as e: except services.errors.account.NoPermissionError as e:
raise Forbidden(str(e)) raise Forbidden(str(e))
@ -416,7 +402,7 @@ class ExternalKnowledgeHitTestingApi(Resource):
metadata_filtering_conditions=payload.metadata_filtering_conditions, metadata_filtering_conditions=payload.metadata_filtering_conditions,
) )
return response return dump_response(ExternalHitTestingResponse, response)
except Exception as e: except Exception as e:
raise InternalServerError(str(e)) raise InternalServerError(str(e))
@ -427,11 +413,7 @@ class BedrockRetrievalApi(Resource):
@console_ns.doc("bedrock_retrieval_test") @console_ns.doc("bedrock_retrieval_test")
@console_ns.doc(description="Bedrock retrieval test (internal use only)") @console_ns.doc(description="Bedrock retrieval test (internal use only)")
@console_ns.expect(console_ns.models[BedrockRetrievalPayload.__name__]) @console_ns.expect(console_ns.models[BedrockRetrievalPayload.__name__])
@console_ns.response( @console_ns.response(200, "Bedrock retrieval test completed", console_ns.models[BedrockRetrievalResponse.__name__])
200,
"Bedrock retrieval test completed",
console_ns.models[ExternalRetrievalTestResponse.__name__],
)
def post(self): def post(self):
payload = BedrockRetrievalPayload.model_validate(console_ns.payload or {}) payload = BedrockRetrievalPayload.model_validate(console_ns.payload or {})
@ -439,4 +421,4 @@ class BedrockRetrievalApi(Resource):
result = ExternalDatasetTestService.knowledge_retrieval( result = ExternalDatasetTestService.knowledge_retrieval(
payload.retrieval_setting, payload.query, payload.knowledge_id payload.retrieval_setting, payload.query, payload.knowledge_id
) )
return result, 200 return dump_response(BedrockRetrievalResponse, result), 200

View File

@ -845,7 +845,7 @@ class DocumentStatusApi(DatasetApiResource):
except ValueError as e: except ValueError as e:
raise InvalidActionError(str(e)) raise InvalidActionError(str(e))
return dump_response(SimpleResultResponse, {"result": "success"}), 200 return SimpleResultResponse(result="success").model_dump(mode="json"), 200
@service_api_ns.route("/datasets/tags") @service_api_ns.route("/datasets/tags")
@ -911,11 +911,8 @@ class DatasetTagsApi(DatasetApiResource):
payload = TagCreatePayload.model_validate(service_api_ns.payload or {}) payload = TagCreatePayload.model_validate(service_api_ns.payload or {})
tag = TagService.save_tags(SaveTagPayload(name=payload.name, type=TagType.KNOWLEDGE), db.session()) tag = TagService.save_tags(SaveTagPayload(name=payload.name, type=TagType.KNOWLEDGE), db.session())
response = dump_response( response = KnowledgeTagResponse(id=tag.id, name=tag.name, type=tag.type, binding_count="0")
KnowledgeTagResponse, return response.model_dump(mode="json"), 200
{"id": tag.id, "name": tag.name, "type": tag.type, "binding_count": 0},
)
return response, 200
@service_api_ns.doc( @service_api_ns.doc(
summary="Update Knowledge Tag", summary="Update Knowledge Tag",
@ -953,11 +950,8 @@ class DatasetTagsApi(DatasetApiResource):
binding_count = TagService.get_tag_binding_count(tag_id, db.session(), tag_type=TagType.KNOWLEDGE) binding_count = TagService.get_tag_binding_count(tag_id, db.session(), tag_type=TagType.KNOWLEDGE)
response = dump_response( response = KnowledgeTagResponse(id=tag.id, name=tag.name, type=tag.type, binding_count=str(binding_count))
KnowledgeTagResponse, return response.model_dump(mode="json"), 200
{"id": tag.id, "name": tag.name, "type": tag.type, "binding_count": binding_count},
)
return response, 200
@service_api_ns.doc( @service_api_ns.doc(
summary="Delete Knowledge Tag", summary="Delete Knowledge Tag",
@ -1088,5 +1082,8 @@ class DatasetTagsBindingStatusApi(DatasetApiResource):
tags = TagService.get_tags_by_target_id( tags = TagService.get_tags_by_target_id(
"knowledge", current_user.current_tenant_id, str(dataset_id), db.session() "knowledge", current_user.current_tenant_id, str(dataset_id), db.session()
) )
tags_list = [{"id": tag.id, "name": tag.name} for tag in tags] response = DatasetBoundTagListResponse(
return dump_response(DatasetBoundTagListResponse, {"data": tags_list, "total": len(tags)}), 200 data=[DatasetBoundTagResponse(id=tag.id, name=tag.name) for tag in tags],
total=len(tags),
)
return response.model_dump(mode="json"), 200

View File

@ -6,14 +6,22 @@ deprecated in generated API docs so clients migrate toward the canonical paths.
""" """
import json import json
from collections.abc import Mapping
from contextlib import ExitStack from contextlib import ExitStack
from copy import deepcopy from copy import deepcopy
from typing import Annotated, Any, Literal, Self, override from typing import Annotated, Any, Literal, Self, override
from uuid import UUID from uuid import UUID
from flask import request, send_file from flask import request, send_file
from pydantic import BaseModel, Field, GetJsonSchemaHandler, WithJsonSchema, field_validator, model_validator from pydantic import (
BaseModel,
Field,
GetJsonSchemaHandler,
ValidationError,
WithJsonSchema,
field_validator,
model_validator,
)
from pydantic.json_schema import SkipJsonSchema
from sqlalchemy import desc, func, select from sqlalchemy import desc, func, select
from werkzeug.exceptions import Forbidden, NotFound from werkzeug.exceptions import Forbidden, NotFound
@ -26,9 +34,10 @@ from controllers.common.errors import (
TooManyFilesError, TooManyFilesError,
UnsupportedFileTypeError, UnsupportedFileTypeError,
) )
from controllers.common.fields import BinaryFileResponse, UrlResponse from controllers.common.fields import UrlResponse
from controllers.common.schema import ( from controllers.common.schema import (
query_params_from_model, query_params_from_model,
query_params_from_request,
register_enum_models, register_enum_models,
register_response_schema_models, register_response_schema_models,
register_schema_models, register_schema_models,
@ -56,6 +65,7 @@ from fields.document_fields import (
DocumentMetadataResponse, DocumentMetadataResponse,
DocumentResponse, DocumentResponse,
DocumentStatusListResponse, DocumentStatusListResponse,
normalize_enum,
) )
from libs.helper import dump_response from libs.helper import dump_response
from libs.login import current_user from libs.login import current_user
@ -281,38 +291,44 @@ class DocumentAndBatchResponse(ResponseModel):
batch: str batch: str
# Use SkipJsonSchema to support 3 metadata modes
class DocumentDetailResponse(ResponseModel): class DocumentDetailResponse(ResponseModel):
id: str id: str
position: int | None = None position: int | SkipJsonSchema[None] = None
data_source_type: str | None = None data_source_type: str | SkipJsonSchema[None] = None
data_source_info: dict[str, Any] | None = Field(default=None) data_source_info: dict[str, Any] | SkipJsonSchema[None] = None
dataset_process_rule_id: str | None = None dataset_process_rule_id: str | None = None
dataset_process_rule: dict[str, Any] | None = Field(default=None) dataset_process_rule: dict[str, Any] | SkipJsonSchema[None] = None
document_process_rule: dict[str, Any] | None = Field(default=None) document_process_rule: dict[str, Any] | SkipJsonSchema[None] = None
name: str | None = None name: str | SkipJsonSchema[None] = None
created_from: str | None = None created_from: str | SkipJsonSchema[None] = None
created_by: str | None = None created_by: str | SkipJsonSchema[None] = None
created_at: int | None = None created_at: int | SkipJsonSchema[None] = None
tokens: int | None = None tokens: int | None = None
indexing_status: str | None = None indexing_status: str | SkipJsonSchema[None] = None
completed_at: int | None = None completed_at: int | None = None
updated_at: int | None = None updated_at: int | None = None
indexing_latency: float | None = None indexing_latency: float | None = None
error: str | None = None error: str | None = None
enabled: bool | None = None enabled: bool | SkipJsonSchema[None] = None
disabled_at: int | None = None disabled_at: int | None = None
disabled_by: str | None = None disabled_by: str | None = None
archived: bool | None = None archived: bool | SkipJsonSchema[None] = None
doc_type: str | None = None doc_type: str | None = None
doc_metadata: list[DocumentMetadataResponse] | None = None doc_metadata: list[DocumentMetadataResponse] | dict[str, Any] | None = None
segment_count: int | None = None segment_count: int | SkipJsonSchema[None] = None
average_segment_length: float | None = None average_segment_length: int | float | SkipJsonSchema[None] = None
hit_count: int | None = None hit_count: int | SkipJsonSchema[None] = None
display_status: str | None = None display_status: str | None = None
doc_form: str | None = None doc_form: str | SkipJsonSchema[None] = None
doc_language: str | None = None doc_language: str | None = None
summary_index_status: str | None = None summary_index_status: str | None = None
need_summary: bool | None = None need_summary: bool | SkipJsonSchema[None] = None
@field_validator("data_source_type", "indexing_status", "display_status", "doc_form", mode="before")
@classmethod
def _normalize_enum_fields(cls, value: Any) -> Any:
return normalize_enum(value)
register_enum_models(service_api_ns, RetrievalMethod) register_enum_models(service_api_ns, RetrievalMethod)
@ -332,7 +348,6 @@ register_schema_models(
) )
register_response_schema_models( register_response_schema_models(
service_api_ns, service_api_ns,
BinaryFileResponse,
UrlResponse, UrlResponse,
DocumentResponse, DocumentResponse,
DocumentAndBatchResponse, DocumentAndBatchResponse,
@ -342,13 +357,13 @@ register_response_schema_models(
) )
def _create_document_by_text(tenant_id: str, dataset_id: UUID) -> tuple[Mapping[str, object], int]: def _create_document_by_text(tenant_id: str, dataset_id: UUID) -> tuple[Document, str]:
"""Create a document from text for both canonical and legacy routes.""" """Create a document from text for both canonical and legacy routes."""
payload = DocumentTextCreatePayload.model_validate(service_api_ns.payload or {}) payload = DocumentTextCreatePayload.model_validate(service_api_ns.payload or {})
args = payload.model_dump(exclude_none=True) args = payload.model_dump(exclude_none=True)
dataset_id_str = str(dataset_id) dataset_id_str = str(dataset_id)
tenant_id_str = str(tenant_id) tenant_id_str = tenant_id
dataset = db.session.scalar( dataset = db.session.scalar(
select(Dataset).where(Dataset.tenant_id == tenant_id_str, Dataset.id == dataset_id_str).limit(1) select(Dataset).where(Dataset.tenant_id == tenant_id_str, Dataset.id == dataset_id_str).limit(1)
) )
@ -407,10 +422,10 @@ def _create_document_by_text(tenant_id: str, dataset_id: UUID) -> tuple[Mapping[
raise ProviderNotInitializeError(ex.description) raise ProviderNotInitializeError(ex.description)
document = documents[0] document = documents[0]
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200 return document, batch
def _update_document_by_text(tenant_id: str, dataset_id: UUID, document_id: UUID) -> tuple[Mapping[str, object], int]: def _update_document_by_text(tenant_id: str, dataset_id: UUID, document_id: UUID) -> tuple[Document, str]:
"""Update a document from text for both canonical and legacy routes.""" """Update a document from text for both canonical and legacy routes."""
payload = DocumentTextUpdate.model_validate(service_api_ns.payload or {}) payload = DocumentTextUpdate.model_validate(service_api_ns.payload or {})
dataset = db.session.scalar( dataset = db.session.scalar(
@ -467,7 +482,7 @@ def _update_document_by_text(tenant_id: str, dataset_id: UUID, document_id: UUID
raise ProviderNotInitializeError(ex.description) raise ProviderNotInitializeError(ex.description)
document = documents[0] document = documents[0]
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200 return document, batch
@service_api_ns.route("/datasets/<uuid:dataset_id>/document/create-by-text") @service_api_ns.route("/datasets/<uuid:dataset_id>/document/create-by-text")
@ -511,7 +526,8 @@ class DocumentAddByTextApi(DatasetApiResource):
@cloud_edition_billing_rate_limit_check("knowledge", "dataset") @cloud_edition_billing_rate_limit_check("knowledge", "dataset")
def post(self, tenant_id: str, dataset_id: UUID): def post(self, tenant_id: str, dataset_id: UUID):
"""Create document by text.""" """Create document by text."""
return _create_document_by_text(tenant_id=tenant_id, dataset_id=dataset_id) document, batch = _create_document_by_text(tenant_id=tenant_id, dataset_id=dataset_id)
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200
@service_api_ns.route("/datasets/<uuid:dataset_id>/document/create_by_text") @service_api_ns.route("/datasets/<uuid:dataset_id>/document/create_by_text")
@ -543,7 +559,8 @@ class DeprecatedDocumentAddByTextApi(DatasetApiResource):
@cloud_edition_billing_rate_limit_check("knowledge", "dataset") @cloud_edition_billing_rate_limit_check("knowledge", "dataset")
def post(self, tenant_id: str, dataset_id: UUID): def post(self, tenant_id: str, dataset_id: UUID):
"""Create document by text through the deprecated underscore alias.""" """Create document by text through the deprecated underscore alias."""
return _create_document_by_text(tenant_id=tenant_id, dataset_id=dataset_id) document, batch = _create_document_by_text(tenant_id=tenant_id, dataset_id=dataset_id)
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200
@service_api_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/update-by-text") @service_api_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/update-by-text")
@ -587,7 +604,8 @@ class DocumentUpdateByTextApi(DatasetApiResource):
@cloud_edition_billing_rate_limit_check("knowledge", "dataset") @cloud_edition_billing_rate_limit_check("knowledge", "dataset")
def post(self, tenant_id: str, dataset_id: UUID, document_id: UUID): def post(self, tenant_id: str, dataset_id: UUID, document_id: UUID):
"""Update document by text.""" """Update document by text."""
return _update_document_by_text(tenant_id=tenant_id, dataset_id=dataset_id, document_id=document_id) document, batch = _update_document_by_text(tenant_id=tenant_id, dataset_id=dataset_id, document_id=document_id)
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200
@service_api_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/update_by_text") @service_api_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/update_by_text")
@ -618,7 +636,8 @@ class DeprecatedDocumentUpdateByTextApi(DatasetApiResource):
@cloud_edition_billing_rate_limit_check("knowledge", "dataset") @cloud_edition_billing_rate_limit_check("knowledge", "dataset")
def post(self, tenant_id: str, dataset_id: UUID, document_id: UUID): def post(self, tenant_id: str, dataset_id: UUID, document_id: UUID):
"""Update document by text through the deprecated underscore alias.""" """Update document by text through the deprecated underscore alias."""
return _update_document_by_text(tenant_id=tenant_id, dataset_id=dataset_id, document_id=document_id) document, batch = _update_document_by_text(tenant_id=tenant_id, dataset_id=dataset_id, document_id=document_id)
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200
@service_api_ns.route( @service_api_ns.route(
@ -767,10 +786,10 @@ class DocumentAddByFileApi(DatasetApiResource):
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200 return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200
def _update_document_by_file(tenant_id: str, dataset_id: UUID, document_id: UUID) -> tuple[Mapping[str, object], int]: def _update_document_by_file(tenant_id: str, dataset_id: UUID, document_id: UUID) -> tuple[Document, str]:
"""Update a document from an uploaded file for canonical and deprecated routes.""" """Update a document from an uploaded file for canonical and deprecated routes."""
dataset_id_str = str(dataset_id) dataset_id_str = str(dataset_id)
tenant_id_str = str(tenant_id) tenant_id_str = tenant_id
dataset = db.session.scalar( dataset = db.session.scalar(
select(Dataset).where(Dataset.tenant_id == tenant_id_str, Dataset.id == dataset_id_str).limit(1) select(Dataset).where(Dataset.tenant_id == tenant_id_str, Dataset.id == dataset_id_str).limit(1)
) )
@ -841,7 +860,7 @@ def _update_document_by_file(tenant_id: str, dataset_id: UUID, document_id: UUID
except ProviderTokenNotInitError as ex: except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description) raise ProviderNotInitializeError(ex.description)
document = documents[0] document = documents[0]
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": document.batch}), 200 return document, document.batch
@service_api_ns.route( @service_api_ns.route(
@ -895,7 +914,8 @@ class DeprecatedDocumentUpdateByFileApi(DatasetApiResource):
@cloud_edition_billing_rate_limit_check("knowledge", "dataset") @cloud_edition_billing_rate_limit_check("knowledge", "dataset")
def post(self, tenant_id: str, dataset_id: UUID, document_id: UUID): def post(self, tenant_id: str, dataset_id: UUID, document_id: UUID):
"""Update document by file through the deprecated file-update aliases.""" """Update document by file through the deprecated file-update aliases."""
return _update_document_by_file(tenant_id=tenant_id, dataset_id=dataset_id, document_id=document_id) document, batch = _update_document_by_file(tenant_id=tenant_id, dataset_id=dataset_id, document_id=document_id)
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200
@service_api_ns.route("/datasets/<uuid:dataset_id>/documents") @service_api_ns.route("/datasets/<uuid:dataset_id>/documents")
@ -928,7 +948,7 @@ class DocumentListApi(DatasetApiResource):
def get(self, tenant_id, dataset_id: UUID): def get(self, tenant_id, dataset_id: UUID):
dataset_id_str = str(dataset_id) dataset_id_str = str(dataset_id)
tenant_id = str(tenant_id) tenant_id = str(tenant_id)
query_params = DocumentListQuery.model_validate(request.args.to_dict()) query_params = query_params_from_request(DocumentListQuery)
dataset = db.session.scalar( dataset = db.session.scalar(
select(Dataset).where(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id_str).limit(1) select(Dataset).where(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id_str).limit(1)
) )
@ -1021,6 +1041,7 @@ class DocumentBatchDownloadZipApi(DatasetApiResource):
) )
cleanup = stack.pop_all() cleanup = stack.pop_all()
response.call_on_close(cleanup.close) response.call_on_close(cleanup.close)
# response-contract:ignore binary send_file response
return response return response
@ -1149,7 +1170,9 @@ class DocumentDownloadApi(DatasetApiResource):
if document.tenant_id != str(tenant_id): if document.tenant_id != str(tenant_id):
raise Forbidden("No permission.") raise Forbidden("No permission.")
return {"url": DocumentService.get_document_download_url(document, db.session())} return UrlResponse(url=DocumentService.get_document_download_url(document, db.session())).model_dump(
mode="json"
)
@service_api_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>") @service_api_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>")
@ -1176,8 +1199,13 @@ class DocumentApi(DatasetApiResource):
) )
@service_api_ns.doc("get_document") @service_api_ns.doc("get_document")
@service_api_ns.doc(description="Get a specific document by ID") @service_api_ns.doc(description="Get a specific document by ID")
@service_api_ns.doc(params={"dataset_id": "Knowledge base ID.", "document_id": "Document ID."}) @service_api_ns.doc(
@service_api_ns.doc(params=query_params_from_model(DocumentGetQuery)) params={
"dataset_id": "Knowledge base ID.",
"document_id": "Document ID.",
**query_params_from_model(DocumentGetQuery),
}
)
@service_api_ns.doc( @service_api_ns.doc(
responses={ responses={
200: "Document retrieved successfully", 200: "Document retrieved successfully",
@ -1205,9 +1233,14 @@ class DocumentApi(DatasetApiResource):
if document.tenant_id != str(tenant_id): if document.tenant_id != str(tenant_id):
raise Forbidden("No permission.") raise Forbidden("No permission.")
metadata = request.args.get("metadata", "all") try:
if metadata not in self.METADATA_CHOICES: query_params = query_params_from_request(DocumentGetQuery)
raise InvalidMetadataError(f"Invalid metadata value: {metadata}") except ValidationError as exc:
metadata = request.args.get("metadata", "all")
raise InvalidMetadataError(f"Invalid metadata value: {metadata}") from exc
metadata = query_params.metadata
response_include: set[str] | None = None
response_exclude: set[str] | None = None
# Calculate summary_index_status if needed # Calculate summary_index_status if needed
summary_index_status = None summary_index_status = None
@ -1221,8 +1254,10 @@ class DocumentApi(DatasetApiResource):
) )
if metadata == "only": if metadata == "only":
response_include = {"id", "doc_type", "doc_metadata"}
response = {"id": document.id, "doc_type": document.doc_type, "doc_metadata": document.doc_metadata_details} response = {"id": document.id, "doc_type": document.doc_type, "doc_metadata": document.doc_metadata_details}
elif metadata == "without": elif metadata == "without":
response_exclude = {"doc_type", "doc_metadata"}
dataset_process_rules = DatasetService.get_process_rules(dataset_id_str, db.session()) dataset_process_rules = DatasetService.get_process_rules(dataset_id_str, db.session())
document_process_rules = document.dataset_process_rule.to_dict() if document.dataset_process_rule else {} document_process_rules = document.dataset_process_rule.to_dict() if document.dataset_process_rule else {}
data_source_info = document.data_source_detail_dict data_source_info = document.data_source_detail_dict
@ -1295,8 +1330,33 @@ class DocumentApi(DatasetApiResource):
"need_summary": document.need_summary if document.need_summary is not None else False, "need_summary": document.need_summary if document.need_summary is not None else False,
} }
return response return DocumentDetailResponse.model_validate(response).model_dump(
mode="json",
include=response_include,
exclude=response_exclude,
)
@service_api_ns.doc(
summary="Update Document by File",
description=(
"Update an existing document by uploading a new file. Re-triggers indexing — use the returned "
"`batch` ID with [Get Document Indexing Status](/api-reference/documents/"
"get-document-indexing-status) to track progress."
),
tags=["Documents"],
responses={
200: "Document updated successfully.",
400: (
"- `too_many_files` : Only one file is allowed.\n"
"- `filename_not_exists_error` : The specified filename does not exist.\n"
"- `provider_not_initialize` : No valid model provider credentials found. Please go to "
"Settings -> Model Provider to complete your provider credentials.\n"
"- `invalid_param` : Knowledge base does not exist, external datasets not supported, "
"file too large, unsupported file type, or invalid doc_form (must be `text_model`, "
"`hierarchical_model`, or `qa_model`)."
),
},
)
@service_api_ns.doc("update_document_by_file") @service_api_ns.doc("update_document_by_file")
@service_api_ns.doc(description="Update an existing document by uploading a file") @service_api_ns.doc(description="Update an existing document by uploading a file")
@service_api_ns.doc(consumes=["multipart/form-data"], params=DOCUMENT_UPDATE_BY_FILE_PARAMS) @service_api_ns.doc(consumes=["multipart/form-data"], params=DOCUMENT_UPDATE_BY_FILE_PARAMS)
@ -1314,7 +1374,8 @@ class DocumentApi(DatasetApiResource):
@cloud_edition_billing_rate_limit_check("knowledge", "dataset") @cloud_edition_billing_rate_limit_check("knowledge", "dataset")
def patch(self, tenant_id: str, dataset_id: UUID, document_id: UUID): def patch(self, tenant_id: str, dataset_id: UUID, document_id: UUID):
"""Update document by file on the canonical document resource.""" """Update document by file on the canonical document resource."""
return _update_document_by_file(tenant_id=tenant_id, dataset_id=dataset_id, document_id=document_id) document, batch = _update_document_by_file(tenant_id=tenant_id, dataset_id=dataset_id, document_id=document_id)
return dump_response(DocumentAndBatchResponse, {"document": document, "batch": batch}), 200
@service_api_ns.doc( @service_api_ns.doc(
summary="Delete Document", summary="Delete Document",

View File

@ -1,7 +1,7 @@
from enum import StrEnum from enum import StrEnum
from typing import Annotated, Literal from typing import Annotated, Literal
from pydantic import BaseModel, Field, WithJsonSchema from pydantic import AliasChoices, BaseModel, Field, WithJsonSchema
class ParentMode(StrEnum): class ParentMode(StrEnum):
@ -26,7 +26,14 @@ class PreProcessingRule(BaseModel):
class Segmentation(BaseModel): class Segmentation(BaseModel):
separator: str = Field(default="\n", description="Custom separator for splitting text.") # TODO: there are internally mismatched / inconsistent naming
# between `separator` and `delimiter` across the codebase.
# Taking `separator` as the canonical.
separator: str = Field(
default="\n",
description="Custom separator for splitting text.",
validation_alias=AliasChoices("separator", "delimiter"),
)
max_tokens: int = Field(description="Maximum token count per chunk.") max_tokens: int = Field(description="Maximum token count per chunk.")
chunk_overlap: int = Field(default=0, description="Token overlap between chunks.") chunk_overlap: int = Field(default=0, description="Token overlap between chunks.")

View File

@ -1,22 +1,9 @@
from datetime import datetime from datetime import datetime
from flask_restx import fields
from pydantic import Field, field_validator from pydantic import Field, field_validator
from fields.base import ResponseModel from fields.base import ResponseModel
from libs.helper import TimestampField, to_timestamp from libs.helper import to_timestamp
dataset_fields = {
"id": fields.String,
"name": fields.String,
"description": fields.String,
"permission": fields.String,
"data_source_type": fields.String,
"indexing_technique": fields.String,
"created_by": fields.String,
"created_at": TimestampField,
"permission_keys": fields.List(fields.String()),
}
class DatasetMetadataResponse(ResponseModel): class DatasetMetadataResponse(ResponseModel):
@ -50,104 +37,6 @@ class DatasetMetadataActionResponse(ResponseModel):
result: str result: str
reranking_model_fields = {"reranking_provider_name": fields.String, "reranking_model_name": fields.String}
keyword_setting_fields = {"keyword_weight": fields.Float}
vector_setting_fields = {
"vector_weight": fields.Float,
"embedding_model_name": fields.String,
"embedding_provider_name": fields.String,
}
weighted_score_fields = {
"weight_type": fields.String,
"keyword_setting": fields.Nested(keyword_setting_fields),
"vector_setting": fields.Nested(vector_setting_fields),
}
dataset_retrieval_model_fields = {
"search_method": fields.String,
"reranking_enable": fields.Boolean,
"reranking_mode": fields.String,
"reranking_model": fields.Nested(reranking_model_fields),
"weights": fields.Nested(weighted_score_fields, allow_null=True),
"top_k": fields.Integer,
"score_threshold_enabled": fields.Boolean,
"score_threshold": fields.Float,
}
dataset_summary_index_fields = {
"enable": fields.Boolean,
"model_name": fields.String,
"model_provider_name": fields.String,
"summary_prompt": fields.String,
}
external_retrieval_model_fields = {
"top_k": fields.Integer,
"score_threshold": fields.Float,
"score_threshold_enabled": fields.Boolean,
}
tag_fields = {"id": fields.String, "name": fields.String, "type": fields.String}
external_knowledge_info_fields = {
"external_knowledge_id": fields.String,
"external_knowledge_api_id": fields.String,
"external_knowledge_api_name": fields.String,
"external_knowledge_api_endpoint": fields.String,
}
doc_metadata_fields = {"id": fields.String, "name": fields.String, "type": fields.String}
icon_info_fields = {
"icon_type": fields.String,
"icon": fields.String,
"icon_background": fields.String,
"icon_url": fields.String,
}
dataset_detail_fields = {
"id": fields.String,
"name": fields.String,
"description": fields.String,
"provider": fields.String,
"permission": fields.String,
"data_source_type": fields.String,
"indexing_technique": fields.String,
"app_count": fields.Integer,
"document_count": fields.Integer,
"word_count": fields.Integer,
"created_by": fields.String,
"author_name": fields.String,
"created_at": TimestampField,
"updated_by": fields.String,
"updated_at": TimestampField,
"embedding_model": fields.String,
"embedding_model_provider": fields.String,
"embedding_available": fields.Boolean,
"retrieval_model_dict": fields.Nested(dataset_retrieval_model_fields),
"summary_index_setting": fields.Nested(dataset_summary_index_fields),
"tags": fields.List(fields.Nested(tag_fields)),
"doc_form": fields.String,
"external_knowledge_info": fields.Nested(external_knowledge_info_fields),
"external_retrieval_model": fields.Nested(external_retrieval_model_fields, allow_null=True),
"doc_metadata": fields.List(fields.Nested(doc_metadata_fields)),
"built_in_field_enabled": fields.Boolean,
"pipeline_id": fields.String,
"runtime_mode": fields.String,
"chunk_structure": fields.String,
"icon_info": fields.Nested(icon_info_fields),
"is_published": fields.Boolean,
"total_documents": fields.Integer,
"total_available_documents": fields.Integer,
"enable_api": fields.Boolean,
"is_multimodal": fields.Boolean,
"permission_keys": fields.List(fields.String()),
}
class DatasetRerankingModelResponse(ResponseModel): class DatasetRerankingModelResponse(ResponseModel):
reranking_provider_name: str | None = None reranking_provider_name: str | None = None
reranking_model_name: str | None = None reranking_model_name: str | None = None

View File

@ -5492,7 +5492,7 @@ Create external knowledge dataset
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 201 | External dataset created successfully | **application/json**: [DatasetDetail](#datasetdetail)<br> | | 201 | External dataset created successfully | **application/json**: [DatasetDetailResponse](#datasetdetailresponse)<br> |
| 400 | Invalid parameters | | | 400 | Invalid parameters | |
| 403 | Permission denied | | | 403 | Permission denied | |
@ -5514,6 +5514,8 @@ Get external knowledge API templates
| 200 | External API templates retrieved successfully | **application/json**: [ExternalKnowledgeApiListResponse](#externalknowledgeapilistresponse)<br> | | 200 | External API templates retrieved successfully | **application/json**: [ExternalKnowledgeApiListResponse](#externalknowledgeapilistresponse)<br> |
### [POST] /datasets/external-knowledge-api ### [POST] /datasets/external-knowledge-api
Create external knowledge API template
#### Request Body #### Request Body
| Required | Schema | | Required | Schema |
@ -5525,6 +5527,7 @@ Get external knowledge API templates
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 201 | External API template created successfully | **application/json**: [ExternalKnowledgeApiResponse](#externalknowledgeapiresponse)<br> | | 201 | External API template created successfully | **application/json**: [ExternalKnowledgeApiResponse](#externalknowledgeapiresponse)<br> |
| 403 | Permission denied | |
### [DELETE] /datasets/external-knowledge-api/{external_knowledge_api_id} ### [DELETE] /datasets/external-knowledge-api/{external_knowledge_api_id}
#### Parameters #### Parameters
@ -5556,11 +5559,13 @@ Get external knowledge API template details
| 404 | Template not found | | | 404 | Template not found | |
### [PATCH] /datasets/external-knowledge-api/{external_knowledge_api_id} ### [PATCH] /datasets/external-knowledge-api/{external_knowledge_api_id}
Update external knowledge API template
#### Parameters #### Parameters
| Name | Located in | Description | Required | Schema | | Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ | | ---- | ---------- | ----------- | -------- | ------ |
| external_knowledge_api_id | path | | Yes | string (uuid) | | external_knowledge_api_id | path | External knowledge API ID | Yes | string (uuid) |
#### Request Body #### Request Body
@ -5573,6 +5578,7 @@ Get external knowledge API template details
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | External API template updated successfully | **application/json**: [ExternalKnowledgeApiResponse](#externalknowledgeapiresponse)<br> | | 200 | External API template updated successfully | **application/json**: [ExternalKnowledgeApiResponse](#externalknowledgeapiresponse)<br> |
| 404 | Template not found | |
### [GET] /datasets/external-knowledge-api/{external_knowledge_api_id}/use-check ### [GET] /datasets/external-knowledge-api/{external_knowledge_api_id}/use-check
Check if external knowledge API is being used Check if external knowledge API is being used
@ -5617,7 +5623,7 @@ Initialize dataset with documents
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 201 | Dataset initialized successfully | **application/json**: [DatasetAndDocumentResponse](#datasetanddocumentresponse)<br> | | 200 | Dataset initialized successfully | **application/json**: [DatasetAndDocumentResponse](#datasetanddocumentresponse)<br> |
| 400 | Invalid request parameters | | | 400 | Invalid request parameters | |
### [GET] /datasets/metadata/built-in ### [GET] /datasets/metadata/built-in
@ -5653,7 +5659,7 @@ Get dataset document processing rules
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | Process rules retrieved successfully | **application/json**: [OpaqueObjectResponse](#opaqueobjectresponse)<br> | | 200 | Process rules retrieved successfully | **application/json**: [ProcessRuleResponse](#processruleresponse)<br> |
### [GET] /datasets/retrieval-setting ### [GET] /datasets/retrieval-setting
Get dataset retrieval settings Get dataset retrieval settings
@ -5774,7 +5780,7 @@ Get dataset auto disable logs
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | Batch indexing estimate calculated successfully | **application/json**: [OpaqueObjectResponse](#opaqueobjectresponse)<br> | | 200 | Indexing estimate calculated successfully | **application/json**: [IndexingEstimateResponse](#indexingestimateresponse)<br> |
### [GET] /datasets/{dataset_id}/batch/{batch}/indexing-status ### [GET] /datasets/{dataset_id}/batch/{batch}/indexing-status
#### Parameters #### Parameters
@ -5862,9 +5868,9 @@ Download selected dataset documents as a single ZIP archive (upload-file only)
#### Responses #### Responses
| Code | Description | Schema | | Code | Description |
| ---- | ----------- | ------ | | ---- | ----------- |
| 200 | ZIP archive generated successfully | **application/json**: [BinaryFileResponse](#binaryfileresponse)<br> | | 200 | ZIP archive downloaded successfully |
### [POST] /datasets/{dataset_id}/documents/generate-summary ### [POST] /datasets/{dataset_id}/documents/generate-summary
**Generate summary index for specified documents** **Generate summary index for specified documents**
@ -5957,7 +5963,7 @@ Get document details
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | Document retrieved successfully | **application/json**: [OpaqueObjectResponse](#opaqueobjectresponse)<br> | | 200 | Document retrieved successfully | **application/json**: [DocumentDetailResponse](#documentdetailresponse)<br> |
| 404 | Document not found | | | 404 | Document not found | |
### [GET] /datasets/{dataset_id}/documents/{document_id}/download ### [GET] /datasets/{dataset_id}/documents/{document_id}/download
@ -5990,7 +5996,7 @@ Estimate document indexing cost
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | Indexing estimate calculated successfully | **application/json**: [OpaqueObjectResponse](#opaqueobjectresponse)<br> | | 200 | Indexing estimate calculated successfully | **application/json**: [IndexingEstimateResponse](#indexingestimateresponse)<br> |
| 400 | Document already finished | | | 400 | Document already finished | |
| 404 | Document not found | | | 404 | Document not found | |
@ -6061,7 +6067,7 @@ Update document metadata
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | Document pipeline execution log retrieved successfully | **application/json**: [OpaqueObjectResponse](#opaqueobjectresponse)<br> | | 200 | Pipeline execution log retrieved successfully | **application/json**: [DocumentPipelineExecutionLogResponse](#documentpipelineexecutionlogresponse)<br> |
### [PATCH] /datasets/{dataset_id}/documents/{document_id}/processing/pause ### [PATCH] /datasets/{dataset_id}/documents/{document_id}/processing/pause
**pause document** **pause document**
@ -6384,6 +6390,7 @@ Returns:
- generating: Number of summaries being generated - generating: Number of summaries being generated
- error: Number of summaries with errors - error: Number of summaries with errors
- not_started: Number of segments without summary records - not_started: Number of segments without summary records
- timeout: Number of summaries that timed out
- summaries: List of summary records with status and content preview - summaries: List of summary records with status and content preview
#### Parameters #### Parameters
@ -6397,7 +6404,7 @@ Returns:
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | Summary status retrieved successfully | **application/json**: [OpaqueObjectResponse](#opaqueobjectresponse)<br> | | 200 | Summary status retrieved successfully | **application/json**: [DocumentSummaryStatusResponse](#documentsummarystatusresponse)<br> |
| 404 | Document not found | | | 404 | Document not found | |
### [GET] /datasets/{dataset_id}/documents/{document_id}/website-sync ### [GET] /datasets/{dataset_id}/documents/{document_id}/website-sync
@ -6451,7 +6458,7 @@ Test external knowledge retrieval for dataset
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | External hit testing completed successfully | **application/json**: [ExternalRetrievalTestResponse](#externalretrievaltestresponse)<br> | | 200 | External hit testing completed successfully | **application/json**: [ExternalHitTestingResponse](#externalhittestingresponse)<br> |
| 400 | Invalid parameters | | | 400 | Invalid parameters | |
| 404 | Dataset not found | | | 404 | Dataset not found | |
@ -9388,7 +9395,7 @@ Bedrock retrieval test (internal use only)
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | Bedrock retrieval test completed | **application/json**: [ExternalRetrievalTestResponse](#externalretrievaltestresponse)<br> | | 200 | Bedrock retrieval test completed | **application/json**: [BedrockRetrievalResponse](#bedrockretrievalresponse)<br> |
### [GET] /trial-apps/{app_id} ### [GET] /trial-apps/{app_id}
**Get app detail** **Get app detail**
@ -15435,6 +15442,21 @@ AppMCPServer Status Enum
| query | string | | Yes | | query | string | | Yes |
| retrieval_setting | [BedrockRetrievalSetting](#bedrockretrievalsetting) | | Yes | | retrieval_setting | [BedrockRetrievalSetting](#bedrockretrievalsetting) | | Yes |
#### BedrockRetrievalRecordResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| content | string | | No |
| metadata | object | | No |
| score | number | | Yes |
| title | string | | No |
#### BedrockRetrievalResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| records | [ [BedrockRetrievalRecordResponse](#bedrockretrievalrecordresponse) ] | | Yes |
#### BedrockRetrievalSetting #### BedrockRetrievalSetting
Retrieval settings for Amazon Bedrock knowledge base queries. Retrieval settings for Amazon Bedrock knowledge base queries.
@ -16325,47 +16347,6 @@ Model class for provider custom model configuration.
| permission | [PermissionEnum](#permissionenum) | | No | | permission | [PermissionEnum](#permissionenum) | | No |
| provider | string, <br>**Default:** vendor | | No | | provider | string, <br>**Default:** vendor | | No |
#### DatasetDetail
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| app_count | integer | | No |
| author_name | string | | No |
| built_in_field_enabled | boolean | | No |
| chunk_structure | string | | No |
| created_at | long | | No |
| created_by | string | | No |
| data_source_type | string | | No |
| description | string | | No |
| doc_form | string | | No |
| doc_metadata | [ [DatasetDocMetadata](#datasetdocmetadata) ] | | No |
| document_count | integer | | No |
| embedding_available | boolean | | No |
| embedding_model | string | | No |
| embedding_model_provider | string | | No |
| enable_api | boolean | | No |
| external_knowledge_info | [ExternalKnowledgeInfo](#externalknowledgeinfo) | | No |
| external_retrieval_model | [ExternalRetrievalModel](#externalretrievalmodel) | | No |
| icon_info | [DatasetIconInfo](#dataseticoninfo) | | No |
| id | string | | No |
| indexing_technique | string | | No |
| is_multimodal | boolean | | No |
| is_published | boolean | | No |
| name | string | | No |
| permission | string | | No |
| permission_keys | [ string ] | | No |
| pipeline_id | string | | No |
| provider | string | | No |
| retrieval_model_dict | [DatasetRetrievalModel](#datasetretrievalmodel) | | No |
| runtime_mode | string | | No |
| summary_index_setting | [_AnonymousInlineModel_b1954337d565](#_anonymousinlinemodel_b1954337d565) | | No |
| tags | [ [Tag](#tag) ] | | No |
| total_available_documents | integer | | No |
| total_documents | integer | | No |
| updated_at | long | | No |
| updated_by | string | | No |
| word_count | integer | | No |
#### DatasetDetailResponse #### DatasetDetailResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -16451,14 +16432,6 @@ Model class for provider custom model configuration.
| updated_by | string | | Yes | | updated_by | string | | Yes |
| word_count | integer | | Yes | | word_count | integer | | Yes |
#### DatasetDocMetadata
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| id | string | | No |
| name | string | | No |
| type | string | | No |
#### DatasetDocMetadataResponse #### DatasetDocMetadataResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -16484,15 +16457,6 @@ Model class for provider custom model configuration.
| score_threshold_enabled | boolean | | No | | score_threshold_enabled | boolean | | No |
| top_k | integer | | Yes | | top_k | integer | | Yes |
#### DatasetIconInfo
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| icon | string | | No |
| icon_background | string | | No |
| icon_type | string | | No |
| icon_url | string | | No |
#### DatasetIconInfoResponse #### DatasetIconInfoResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -16502,12 +16466,6 @@ Model class for provider custom model configuration.
| icon_type | string | | No | | icon_type | string | | No |
| icon_url | string | | No | | icon_url | string | | No |
#### DatasetKeywordSetting
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| keyword_weight | number | | No |
#### DatasetKeywordSettingResponse #### DatasetKeywordSettingResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -16645,13 +16603,6 @@ Model class for provider custom model configuration.
| page | integer | | Yes | | page | integer | | Yes |
| total | integer | | Yes | | total | integer | | Yes |
#### DatasetRerankingModel
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| reranking_model_name | string | | No |
| reranking_provider_name | string | | No |
#### DatasetRerankingModelResponse #### DatasetRerankingModelResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -16672,19 +16623,6 @@ Model class for provider custom model configuration.
| name | string | | Yes | | name | string | | Yes |
| permission | string | | No | | permission | string | | No |
#### DatasetRetrievalModel
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| reranking_enable | boolean | | No |
| reranking_mode | string | | No |
| reranking_model | [DatasetRerankingModel](#datasetrerankingmodel) | | No |
| score_threshold | number | | No |
| score_threshold_enabled | boolean | | No |
| search_method | string | | No |
| top_k | integer | | No |
| weights | [DatasetWeightedScore](#datasetweightedscore) | | No |
#### DatasetRetrievalModelResponse #### DatasetRetrievalModelResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -16734,14 +16672,6 @@ Model class for provider custom model configuration.
| retrieval_model | object | | No | | retrieval_model | object | | No |
| summary_index_setting | object | | No | | summary_index_setting | object | | No |
#### DatasetVectorSetting
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| embedding_model_name | string | | No |
| embedding_provider_name | string | | No |
| vector_weight | number | | No |
#### DatasetVectorSettingResponse #### DatasetVectorSettingResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -16750,14 +16680,6 @@ Model class for provider custom model configuration.
| embedding_provider_name | string | | No | | embedding_provider_name | string | | No |
| vector_weight | number | | No | | vector_weight | number | | No |
#### DatasetWeightedScore
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| keyword_setting | [DatasetKeywordSetting](#datasetkeywordsetting) | | No |
| vector_setting | [DatasetVectorSetting](#datasetvectorsetting) | | No |
| weight_type | string | | No |
#### DatasetWeightedScoreResponse #### DatasetWeightedScoreResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -17061,6 +16983,42 @@ Request payload for bulk downloading documents as a zip archive.
| ---- | ---- | ----------- | -------- | | ---- | ---- | ----------- | -------- |
| document_ids | [ string (uuid) ] | List of document IDs to include in the ZIP download. | Yes | | document_ids | [ string (uuid) ] | List of document IDs to include in the ZIP download. | Yes |
#### DocumentDetailResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| archived | boolean | | No |
| average_segment_length | number | | No |
| completed_at | integer | | No |
| created_at | integer | | No |
| created_by | string | | No |
| created_from | string | | No |
| data_source_detail_dict | | | No |
| data_source_info | | | No |
| data_source_type | string | | No |
| dataset_process_rule | | | No |
| dataset_process_rule_id | string | | No |
| disabled_at | integer | | No |
| disabled_by | string | | No |
| display_status | string | | No |
| doc_form | string | | No |
| doc_language | string | | No |
| doc_metadata | [ [DocumentMetadataResponse](#documentmetadataresponse) ] | | No |
| doc_type | string | | No |
| document_process_rule | | | No |
| enabled | boolean | | No |
| error | string | | No |
| hit_count | integer | | No |
| id | string | | Yes |
| indexing_latency | number | | No |
| indexing_status | string | | No |
| name | string | | No |
| need_summary | boolean | | No |
| position | integer | | No |
| segment_count | integer | | No |
| tokens | integer | | No |
| updated_at | integer | | No |
#### DocumentMetadataOperation #### DocumentMetadataOperation
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -17085,6 +17043,15 @@ Request payload for bulk downloading documents as a zip archive.
| doc_metadata | | | No | | doc_metadata | | | No |
| doc_type | string | | No | | doc_type | string | | No |
#### DocumentPipelineExecutionLogResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| datasource_info | [JsonValue](#jsonvalue) | | No |
| datasource_node_id | string | | No |
| datasource_type | string | | No |
| input_data | [JsonValue](#jsonvalue) | | No |
#### DocumentRenamePayload #### DocumentRenamePayload
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -17149,6 +17116,14 @@ Request payload for bulk downloading documents as a zip archive.
| stopped_at | integer | | Yes | | stopped_at | integer | | Yes |
| total_segments | integer | | No | | total_segments | integer | | No |
#### DocumentSummaryStatusResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| summaries | [ [SummaryEntryResponse](#summaryentryresponse) ] | | Yes |
| summary_status | [SummaryStatusResponse](#summarystatusresponse) | | Yes |
| total_segments | integer | | Yes |
#### DocumentWithSegmentsListResponse #### DocumentWithSegmentsListResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -17679,6 +17654,35 @@ Built-in tool icons are URL strings; API-based tool icons are provider-defined p
| metadata_filtering_conditions | object | | No | | metadata_filtering_conditions | object | | No |
| query | string | | Yes | | query | string | | Yes |
#### ExternalHitTestingQueryResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| content | string | | Yes |
#### ExternalHitTestingRecordResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| content | string | | No |
| metadata | object | | No |
| score | number | | No |
| title | string | | No |
#### ExternalHitTestingResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| query | [ExternalHitTestingQueryResponse](#externalhittestingqueryresponse) | | Yes |
| records | [ [ExternalHitTestingRecordResponse](#externalhittestingrecordresponse) ] | | Yes |
#### ExternalKnowledgeApiBindingResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| id | string | | Yes |
| name | string | | Yes |
#### ExternalKnowledgeApiListResponse #### ExternalKnowledgeApiListResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -17702,43 +17706,13 @@ Built-in tool icons are URL strings; API-based tool icons are provider-defined p
| ---- | ---- | ----------- | -------- | | ---- | ---- | ----------- | -------- |
| created_at | string | | Yes | | created_at | string | | Yes |
| created_by | string | | Yes | | created_by | string | | Yes |
| dataset_bindings | [ [ExternalKnowledgeDatasetBindingResponse](#externalknowledgedatasetbindingresponse) ] | | No | | dataset_bindings | [ [ExternalKnowledgeApiBindingResponse](#externalknowledgeapibindingresponse) ] | | Yes |
| description | string | | Yes | | description | string | | Yes |
| id | string | | Yes | | id | string | | Yes |
| name | string | | Yes | | name | string | | Yes |
| settings | object | | No | | settings | object | | Yes |
| tenant_id | string | | Yes | | tenant_id | string | | Yes |
#### ExternalKnowledgeDatasetBindingResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| id | string | | Yes |
| name | string | | Yes |
#### ExternalKnowledgeInfo
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| external_knowledge_api_endpoint | string | | No |
| external_knowledge_api_id | string | | No |
| external_knowledge_api_name | string | | No |
| external_knowledge_id | string | | No |
#### ExternalRetrievalModel
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| score_threshold | number | | No |
| score_threshold_enabled | boolean | | No |
| top_k | integer | | No |
#### ExternalRetrievalTestResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| ExternalRetrievalTestResponse | object<br>[ object ] | | |
#### FeatureModel #### FeatureModel
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -18243,27 +18217,15 @@ Query parameter for including secret variables in export.
| info_list | object | | Yes | | info_list | object | | Yes |
| process_rule | object | | Yes | | process_rule | object | | Yes |
#### IndexingEstimatePreviewItemResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| child_chunks | [ string ] | | No |
| content | string | | Yes |
| summary | string | | No |
#### IndexingEstimateQaPreviewItemResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| answer | string | | Yes |
| question | string | | Yes |
#### IndexingEstimateResponse #### IndexingEstimateResponse
| Name | Type | Description | Required | | Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- | | ---- | ---- | ----------- | -------- |
| preview | [ [IndexingEstimatePreviewItemResponse](#indexingestimatepreviewitemresponse) ] | | Yes | | currency | string | | Yes |
| qa_preview | [ [IndexingEstimateQaPreviewItemResponse](#indexingestimateqapreviewitemresponse) ] | | No | | preview | [ [PreviewDetail](#previewdetail) ] | | Yes |
| qa_preview | [ [QAPreviewDetail](#qapreviewdetail) ] | | No |
| tokens | integer | | Yes |
| total_price | number<br>integer | | Yes |
| total_segments | integer | | Yes | | total_segments | integer | | Yes |
#### InfoList #### InfoList
@ -19259,12 +19221,6 @@ OAuth schema
| redirect_uri | string | | No | | redirect_uri | string | | No |
| refresh_token | string | | No | | refresh_token | string | | No |
#### OpaqueObjectResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| OpaqueObjectResponse | object | | |
#### Option #### Option
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -20310,6 +20266,14 @@ Dataset Process Rule Mode
| ---- | ---- | ----------- | -------- | | ---- | ---- | ----------- | -------- |
| ProcessRuleMode | string | Dataset Process Rule Mode | | | ProcessRuleMode | string | Dataset Process Rule Mode | |
#### ProcessRuleResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| limits | object | | Yes |
| mode | [ProcessRuleMode](#processrulemode) | | Yes |
| rules | [Rule](#rule) | | No |
#### ProviderConfig #### ProviderConfig
Model class for common provider settings like credentials Model class for common provider settings like credentials
@ -21581,6 +21545,28 @@ The subscription constructor of the trigger provider
| ---- | ---- | ----------- | -------- | | ---- | ---- | ----------- | -------- |
| data | [ string ] | | Yes | | data | [ string ] | | Yes |
#### SummaryEntryResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| error | string | | No |
| segment_id | string | | Yes |
| segment_position | integer | | Yes |
| status | string | | Yes |
| summary_preview | string | | No |
| updated_at | integer | | No |
#### SummaryStatusResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| completed | integer | | No |
| error | integer | | No |
| generating | integer | | No |
| not_started | integer | | No |
| timeout | integer | | No |
#### SwitchWorkspacePayload #### SwitchWorkspacePayload
| Name | Type | Description | Required | | Name | Type | Description | Required |
@ -23702,15 +23688,6 @@ Workflow tool configuration
| data | [ [AccessPolicy](#accesspolicy) ] | | No | | data | [ [AccessPolicy](#accesspolicy) ] | | No |
| pagination | [Pagination](#pagination) | | No | | pagination | [Pagination](#pagination) | | No |
#### _AnonymousInlineModel_b1954337d565
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| enable | boolean | | No |
| model_name | string | | No |
| model_provider_name | string | | No |
| summary_prompt | string | | No |
#### _DeleteMemberBindingsRequest #### _DeleteMemberBindingsRequest
| Name | Type | Description | Required | | Name | Type | Description | Required |

View File

@ -46,76 +46,6 @@ Deprecated legacy alias for creating a new document by providing text content. U
| 401 | Unauthorized - invalid API token | | | 401 | Unauthorized - invalid API token | |
| 403 | Forbidden - dataset API access or workspace access denied | | | 403 | Forbidden - dataset API access or workspace access denied | |
### [DELETE] /datasets/{dataset_id}/documents/{document_id}
**Delete Document**
Permanently delete a document and all its chunks from the knowledge base.
#### Parameters
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| dataset_id | path | Knowledge base ID. | Yes | string (uuid) |
| document_id | path | Document ID. | Yes | string (uuid) |
#### Responses
| Code | Description |
| ---- | ----------- |
| 204 | Success. |
| 400 | `document_indexing` : Cannot delete document during indexing. |
| 401 | Unauthorized - invalid API token |
| 403 | `archived_document_immutable` : The archived document is not editable. |
| 404 | `not_found` : Document Not Exists. |
### [GET] /datasets/{dataset_id}/documents/{document_id}
**Get Document**
Retrieve detailed information about a specific document, including its indexing status, metadata, and processing statistics.
#### Parameters
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| dataset_id | path | Knowledge base ID. | Yes | string (uuid) |
| document_id | path | Document ID. | Yes | string (uuid) |
| metadata | query | `all` returns all fields including metadata. `only` returns only `id`, `doc_type`, and `doc_metadata`. `without` returns all fields except `doc_metadata`. | No | string, <br>**Available values:** "all", "only", "without", <br>**Default:** all |
#### Responses
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Document details. The response shape varies based on the `metadata` query parameter. When `metadata` is `only`, only `id`, `doc_type`, and `doc_metadata` are returned. When `metadata` is `without`, `doc_type` and `doc_metadata` are omitted. | **application/json**: [DocumentDetailResponse](#documentdetailresponse)<br> |
| 400 | `invalid_metadata` : Invalid metadata value for the specified key. | |
| 401 | Unauthorized - invalid API token | |
| 403 | `forbidden` : No permission. | |
| 404 | `not_found` : Document not found. | |
### [PATCH] /datasets/{dataset_id}/documents/{document_id}
Update an existing document by uploading a file
#### Parameters
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| dataset_id | path | Knowledge base ID. | Yes | string (uuid) |
| document_id | path | Document ID. | Yes | string (uuid) |
#### Request Body
| Required | Schema |
| -------- | ------ |
| No | **multipart/form-data**: { **"data"**: string, **"file"**: binary }<br> |
#### Responses
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Document updated successfully | **application/json**: [DocumentAndBatchResponse](#documentandbatchresponse)<br> |
| 401 | Unauthorized - invalid API token | |
| 403 | Forbidden - dataset API access or workspace access denied | |
| 404 | Document not found | |
### ~~[POST] /datasets/{dataset_id}/documents/{document_id}/update_by_text~~ ### ~~[POST] /datasets/{dataset_id}/documents/{document_id}/update_by_text~~
***DEPRECATED*** ***DEPRECATED***
@ -1439,7 +1369,9 @@ Retrieve detailed information about a specific document, including its indexing
| 404 | `not_found` : Document not found. | | | 404 | `not_found` : Document not found. | |
### [PATCH] /datasets/{dataset_id}/documents/{document_id} ### [PATCH] /datasets/{dataset_id}/documents/{document_id}
Update an existing document by uploading a file **Update Document by File**
Update an existing document by uploading a new file. Re-triggers indexing — use the returned `batch` ID with [Get Document Indexing Status](/api-reference/documents/get-document-indexing-status) to track progress.
#### Parameters #### Parameters
@ -1458,7 +1390,8 @@ Update an existing document by uploading a file
| Code | Description | Schema | | Code | Description | Schema |
| ---- | ----------- | ------ | | ---- | ----------- | ------ |
| 200 | Document updated successfully | **application/json**: [DocumentAndBatchResponse](#documentandbatchresponse)<br> | | 200 | Document updated successfully. | **application/json**: [DocumentAndBatchResponse](#documentandbatchresponse)<br> |
| 400 | - `too_many_files` : Only one file is allowed. - `filename_not_exists_error` : The specified filename does not exist. - `provider_not_initialize` : No valid model provider credentials found. Please go to Settings -> Model Provider to complete your provider credentials. - `invalid_param` : Knowledge base does not exist, external datasets not supported, file too large, unsupported file type, or invalid doc_form (must be `text_model`, `hierarchical_model`, or `qa_model`). | |
| 401 | Unauthorized - invalid API token | | | 401 | Unauthorized - invalid API token | |
| 403 | Forbidden - dataset API access or workspace access denied | | | 403 | Forbidden - dataset API access or workspace access denied | |
| 404 | Document not found | | | 404 | Document not found | |
@ -2995,7 +2928,7 @@ Request payload for bulk downloading documents as a zip archive.
| Name | Type | Description | Required | | Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- | | ---- | ---- | ----------- | -------- |
| archived | boolean | | No | | archived | boolean | | No |
| average_segment_length | number | | No | | average_segment_length | integer<br>number | | No |
| completed_at | integer | | No | | completed_at | integer | | No |
| created_at | integer | | No | | created_at | integer | | No |
| created_by | string | | No | | created_by | string | | No |
@ -3009,7 +2942,7 @@ Request payload for bulk downloading documents as a zip archive.
| display_status | string | | No | | display_status | string | | No |
| doc_form | string | | No | | doc_form | string | | No |
| doc_language | string | | No | | doc_language | string | | No |
| doc_metadata | [ [DocumentMetadataResponse](#documentmetadataresponse) ] | | No | | doc_metadata | [ [DocumentMetadataResponse](#documentmetadataresponse) ]<br>object | | No |
| doc_type | string | | No | | doc_type | string | | No |
| document_process_rule | object | | No | | document_process_rule | object | | No |
| enabled | boolean | | No | | enabled | boolean | | No |

View File

@ -31,6 +31,7 @@ from controllers.console.datasets.datasets import (
DatasetUseCheckApi, DatasetUseCheckApi,
) )
from controllers.console.datasets.error import DatasetInUseError, DatasetNameDuplicateError, IndexingEstimateError from controllers.console.datasets.error import DatasetInUseError, DatasetNameDuplicateError, IndexingEstimateError
from core.entities.knowledge_entities import IndexingEstimate
from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError
from core.provider_manager import ProviderManager from core.provider_manager import ProviderManager
from core.rag.index_processor.constant.index_type import IndexStructureType from core.rag.index_processor.constant.index_type import IndexStructureType
@ -1391,8 +1392,7 @@ class TestDatasetIndexingEstimateApi:
mock_file = self._upload_file() mock_file = self._upload_file()
mock_response = MagicMock() mock_response = IndexingEstimate(total_segments=100, preview=[])
mock_response.model_dump.return_value = {"tokens": 100}
with ( with (
app.test_request_context("/"), app.test_request_context("/"),
@ -1418,7 +1418,13 @@ class TestDatasetIndexingEstimateApi:
response, status = method(api, "tenant-1") response, status = method(api, "tenant-1")
assert status == 200 assert status == 200
assert response == {"tokens": 100} assert response == {
"tokens": 0,
"total_price": 0,
"currency": "USD",
"total_segments": 100,
"preview": [],
}
def test_post_file_not_found(self, app: Flask): def test_post_file_not_found(self, app: Flask):
api = DatasetIndexingEstimateApi() api = DatasetIndexingEstimateApi()

View File

@ -1,3 +1,4 @@
import datetime
import inspect import inspect
from unittest.mock import ANY, MagicMock, patch from unittest.mock import ANY, MagicMock, patch
@ -34,6 +35,7 @@ from controllers.console.datasets.error import (
InvalidActionError, InvalidActionError,
InvalidMetadataError, InvalidMetadataError,
) )
from core.entities.knowledge_entities import IndexingEstimate
from core.rag.index_processor.constant.index_type import IndexStructureType from core.rag.index_processor.constant.index_type import IndexStructureType
from models.dataset import Dataset from models.dataset import Dataset
from models.dataset import Document as DatasetDocument from models.dataset import Document as DatasetDocument
@ -72,8 +74,46 @@ def make_serializable_document(**overrides):
} }
attrs.update(overrides) attrs.update(overrides)
document = MagicMock(spec_set=list(attrs)) document = MagicMock(spec_set=list(attrs))
for name, value in attrs.items(): document.configure_mock(**attrs)
setattr(document, name, value) return document
def make_document_detail(**overrides):
attrs = {
"id": "doc-1",
"position": 1,
"data_source_type": "upload_file",
"data_source_info_dict": {"upload_file_id": "file-1"},
"data_source_detail_dict": {},
"dataset_process_rule_id": None,
"dataset_process_rule": None,
"name": "Document",
"created_from": "web",
"created_by": "u1",
"created_at": datetime.datetime(2024, 1, 1, tzinfo=datetime.UTC),
"tokens": 10,
"indexing_status": "completed",
"completed_at": None,
"updated_at": None,
"indexing_latency": None,
"error": None,
"enabled": True,
"disabled_at": None,
"disabled_by": None,
"archived": False,
"doc_type": "others",
"doc_metadata_details": [],
"segment_count": 0,
"average_segment_length": 0,
"hit_count": 0,
"display_status": "available",
"doc_form": "text_model",
"doc_language": "English",
"need_summary": False,
}
attrs.update(overrides)
document = MagicMock(spec_set=list(attrs))
document.configure_mock(**attrs)
return document return document
@ -173,6 +213,42 @@ class TestGetProcessRuleApi:
assert "rules" in response assert "rules" in response
def test_get_with_document_preserves_legacy_segmentation_delimiter(self, app: Flask, patch_tenant):
api = GetProcessRuleApi()
method = inspect.unwrap(api.get)
user, _ = patch_tenant
document = MagicMock(dataset_id="ds-1")
process_rule = MagicMock(
mode="custom",
rules_dict={"segmentation": {"delimiter": "---", "max_tokens": 123}},
)
with (
app.test_request_context("/?document_id=doc-1"),
patch(
"controllers.console.datasets.datasets_document.db.get_or_404",
return_value=document,
),
patch(
"controllers.console.datasets.datasets_document.DatasetService.get_dataset",
return_value=MagicMock(),
),
patch(
"controllers.console.datasets.datasets_document.DatasetService.check_dataset_permission",
return_value=None,
),
patch(
"controllers.console.datasets.datasets_document.db.session.scalar",
return_value=process_rule,
),
):
response = method(api, user)
assert response["rules"]["segmentation"]["separator"] == "---"
assert response["rules"]["segmentation"]["max_tokens"] == 123
assert "delimiter" not in response["rules"]["segmentation"]
def test_get_with_document_dataset_not_found(self, app: Flask, patch_tenant): def test_get_with_document_dataset_not_found(self, app: Flask, patch_tenant):
api = GetProcessRuleApi() api = GetProcessRuleApi()
method = inspect.unwrap(api.get) method = inspect.unwrap(api.get)
@ -414,7 +490,7 @@ class TestDocumentApi:
method = inspect.unwrap(api.get) method = inspect.unwrap(api.get)
user, tenant_id = patch_tenant user, tenant_id = patch_tenant
document = MagicMock(dataset_process_rule=None) document = make_document_detail()
with ( with (
app.test_request_context("/"), app.test_request_context("/"),
@ -926,12 +1002,24 @@ class TestDocumentSummaryStatusApi:
), ),
patch( patch(
"services.summary_index_service.SummaryIndexService.get_document_summary_status_detail", "services.summary_index_service.SummaryIndexService.get_document_summary_status_detail",
return_value={"total_segments": 0}, return_value={
"total_segments": 1,
"summary_status": {"timeout": 1},
"summaries": [
{
"segment_id": "segment-1",
"segment_position": 1,
"status": "timeout",
}
],
},
), ),
): ):
response, status = method(api, user, "ds-1", "doc-1") response, status = method(api, user, "ds-1", "doc-1")
assert status == 200 assert status == 200
assert response["summary_status"]["timeout"] == 1
assert response["summaries"][0]["status"] == "timeout"
class TestDocumentIndexingEstimateApi: class TestDocumentIndexingEstimateApi:
@ -1103,7 +1191,7 @@ class TestDocumentBatchIndexingEstimateApi:
patch.object(api, "get_batch_documents", return_value=[doc]), patch.object(api, "get_batch_documents", return_value=[doc]),
patch( patch(
"controllers.console.datasets.datasets_document.IndexingRunner.indexing_estimate", "controllers.console.datasets.datasets_document.IndexingRunner.indexing_estimate",
return_value=MagicMock(model_dump=lambda: {"tokens": 2}), return_value=IndexingEstimate(total_segments=2, preview=[]),
), ),
): ):
resp, status = method(api, tenant_id, user, "ds-1", "batch-1") resp, status = method(api, tenant_id, user, "ds-1", "batch-1")
@ -1132,7 +1220,7 @@ class TestDocumentBatchIndexingEstimateApi:
patch.object(api, "get_batch_documents", return_value=[doc]), patch.object(api, "get_batch_documents", return_value=[doc]),
patch( patch(
"controllers.console.datasets.datasets_document.IndexingRunner.indexing_estimate", "controllers.console.datasets.datasets_document.IndexingRunner.indexing_estimate",
return_value=MagicMock(model_dump=lambda: {"tokens": 1}), return_value=IndexingEstimate(total_segments=1, preview=[]),
), ),
): ):
resp, status = method(api, tenant_id, user, "ds-1", "batch-1") resp, status = method(api, tenant_id, user, "ds-1", "batch-1")
@ -1314,7 +1402,7 @@ class TestDocumentApiMetadata:
method = inspect.unwrap(api.get) method = inspect.unwrap(api.get)
user, tenant_id = patch_tenant user, tenant_id = patch_tenant
document = MagicMock(dataset_process_rule=None, doc_metadata_details=[]) document = make_document_detail(doc_metadata_details=[])
with ( with (
app.test_request_context("/?metadata=only"), app.test_request_context("/?metadata=only"),
@ -1334,7 +1422,7 @@ class TestDocumentApiMetadata:
method = inspect.unwrap(api.get) method = inspect.unwrap(api.get)
user, tenant_id = patch_tenant user, tenant_id = patch_tenant
document = MagicMock(dataset_process_rule=None) document = make_document_detail()
with ( with (
app.test_request_context("/?metadata=without"), app.test_request_context("/?metadata=without"),
@ -1611,7 +1699,7 @@ class TestDocumentIndexingEdgeCases:
), ),
patch( patch(
"controllers.console.datasets.datasets_document.IndexingRunner.indexing_estimate", "controllers.console.datasets.datasets_document.IndexingRunner.indexing_estimate",
return_value=MagicMock(model_dump=lambda: {"tokens": 5}), return_value=IndexingEstimate(total_segments=5, preview=[]),
), ),
): ):
response, status = method(api, tenant_id, user, "ds-1", "doc-1") response, status = method(api, tenant_id, user, "ds-1", "doc-1")

View File

@ -1,5 +1,6 @@
import inspect import inspect
from types import SimpleNamespace from types import SimpleNamespace
from typing import Any
from unittest.mock import ANY, MagicMock, PropertyMock, patch from unittest.mock import ANY, MagicMock, PropertyMock, patch
import pytest import pytest
@ -8,7 +9,6 @@ from werkzeug.exceptions import Forbidden, NotFound
import services import services
from controllers.console import console_ns from controllers.console import console_ns
from controllers.console.datasets import external as external_module
from controllers.console.datasets.error import DatasetNameDuplicateError from controllers.console.datasets.error import DatasetNameDuplicateError
from controllers.console.datasets.external import ( from controllers.console.datasets.external import (
BedrockRetrievalApi, BedrockRetrievalApi,
@ -40,28 +40,185 @@ def current_user() -> Account:
return user return user
def _external_api_dict(api_id: str = "api-1") -> dict:
return {
"id": api_id,
"tenant_id": "tenant-1",
"name": f"External API {api_id}",
"description": f"Description for {api_id}",
"settings": {
"endpoint": f"https://external.example.com/{api_id}",
"api_key": "secret",
"headers": {"X-Source": "unit-test"},
"timeout": 30,
},
"dataset_bindings": [
{"id": f"dataset-{api_id}", "name": f"Dataset {api_id}"},
],
"created_by": "user-1",
"created_at": "2024-01-01T00:00:00",
}
def _external_api_object(api_id: str = "api-1") -> SimpleNamespace:
payload = _external_api_dict(api_id)
return SimpleNamespace(
**{
**payload,
"dataset_bindings": [SimpleNamespace(**binding) for binding in payload["dataset_bindings"]],
}
)
def _expected_dataset_detail_payload() -> dict[str, Any]:
return {
"id": "dataset-1",
"name": "Support knowledge",
"description": "External support articles",
"provider": "external",
"permission": "only_me",
"data_source_type": "external",
"indexing_technique": "economy",
"app_count": 2,
"document_count": 7,
"word_count": 2048,
"created_by": "user-1",
"author_name": "Test User",
"created_at": 1710000000,
"updated_by": "user-2",
"updated_at": 1710003600,
"embedding_model": None,
"embedding_model_provider": None,
"embedding_available": False,
"retrieval_model_dict": {
"search_method": "semantic_search",
"reranking_enable": False,
"reranking_mode": None,
"reranking_model": {"reranking_provider_name": None, "reranking_model_name": None},
"weights": None,
"top_k": 4,
"score_threshold_enabled": True,
"score_threshold": 0.5,
},
"summary_index_setting": {
"enable": True,
"model_name": "summary-model",
"model_provider_name": "provider-a",
"summary_prompt": "Summarize this.",
},
"tags": [{"id": "tag-1", "name": "Support", "type": "knowledge"}],
"doc_form": "text_model",
"external_knowledge_info": {
"external_knowledge_id": "knowledge-1",
"external_knowledge_api_id": "api-1",
"external_knowledge_api_name": "External API api-1",
"external_knowledge_api_endpoint": "https://external.example.com/api-1",
},
"external_retrieval_model": {
"top_k": 4,
"score_threshold": 0.5,
"score_threshold_enabled": True,
},
"doc_metadata": [{"id": "metadata-1", "name": "source", "type": "string"}],
"built_in_field_enabled": True,
"pipeline_id": None,
"runtime_mode": "external",
"chunk_structure": "general",
"icon_info": {
"icon_type": "emoji",
"icon": "book",
"icon_background": "#FFF4ED",
"icon_url": None,
},
"is_published": True,
"total_documents": 7,
"total_available_documents": 6,
"enable_api": True,
"is_multimodal": False,
"maintainer": None,
"permission_keys": [],
}
def _dataset_detail_object() -> SimpleNamespace:
payload = _expected_dataset_detail_payload()
return SimpleNamespace(
**{
**payload,
"summary_index_setting": SimpleNamespace(**payload["summary_index_setting"]),
"tags": [SimpleNamespace(**tag) for tag in payload["tags"]],
"external_knowledge_info": SimpleNamespace(**payload["external_knowledge_info"]),
"external_retrieval_model": SimpleNamespace(**payload["external_retrieval_model"]),
"doc_metadata": [SimpleNamespace(**item) for item in payload["doc_metadata"]],
"icon_info": SimpleNamespace(**payload["icon_info"]),
}
)
class TestExternalApiTemplateListApi: class TestExternalApiTemplateListApi:
def test_get_success(self, app: Flask): def test_get_success(self, app: Flask):
api = ExternalApiTemplateListApi() api = ExternalApiTemplateListApi()
method = inspect.unwrap(api.get) method = inspect.unwrap(api.get)
api_item = MagicMock() api_item = _external_api_object("api-1")
api_item.to_dict.return_value = {"id": "1"}
with ( with (
app.test_request_context("/?page=1&limit=20"), app.test_request_context("/?page=2&limit=1&keyword=vector"),
patch.object( patch.object(
ExternalDatasetService, ExternalDatasetService,
"get_external_knowledge_apis", "get_external_knowledge_apis",
return_value=([api_item], 1), return_value=([api_item], 3),
) as get_external_knowledge_apis, ) as get_external_knowledge_apis,
): ):
resp, status = method(api, "tenant-1") resp, status = method(api, "tenant-1")
assert status == 200 assert status == 200
assert resp["total"] == 1 assert resp == {
assert resp["data"][0]["id"] == "1" "data": [_external_api_dict("api-1")],
get_external_knowledge_apis.assert_called_once_with(1, 20, "tenant-1", None) "has_more": True,
"limit": 1,
"total": 3,
"page": 2,
}
get_external_knowledge_apis.assert_called_once_with(2, 1, "tenant-1", "vector")
def test_post_success_uses_validated_payload_and_returns_template(self, app: Flask, current_user: Account):
api = ExternalApiTemplateListApi()
method = inspect.unwrap(api.post)
payload = {
"name": "Vendor Search",
"settings": {
"endpoint": "https://external.example.com/search",
"api_key": "secret",
"headers": {"X-Source": "unit-test"},
"timeout": 30,
},
}
created = _external_api_object("api-created")
session = MagicMock()
with (
app.test_request_context("/", json=payload),
patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload),
patch.object(ExternalDatasetService, "validate_api_list") as validate_api_list,
patch.object(
ExternalDatasetService,
"create_external_knowledge_api",
return_value=created,
) as create_external_knowledge_api,
):
resp, status = method(api, session, "tenant-1", current_user)
assert status == 201
assert resp == _external_api_dict("api-created")
validate_api_list.assert_called_once_with(payload["settings"])
create_external_knowledge_api.assert_called_once_with(
tenant_id="tenant-1",
user_id="user-1",
args=payload,
session=session,
)
def test_post_forbidden(self, app: Flask, current_user: Account): def test_post_forbidden(self, app: Flask, current_user: Account):
current_user.role = TenantAccountRole.NORMAL current_user.role = TenantAccountRole.NORMAL
@ -99,6 +256,28 @@ class TestExternalApiTemplateListApi:
class TestExternalApiTemplateApi: class TestExternalApiTemplateApi:
def test_get_success_returns_template_contract(self, app: Flask):
api = ExternalApiTemplateApi()
method = inspect.unwrap(api.get)
template = _external_api_object("api-detail")
session = MagicMock()
with (
app.test_request_context("/"),
patch.object(
ExternalDatasetService,
"get_external_knowledge_api",
return_value=template,
) as get_external_knowledge_api,
):
resp, status = method(api, session, "tenant-1", "api-detail")
assert status == 200
assert resp == _external_api_dict("api-detail")
get_external_knowledge_api.assert_called_once_with(
external_knowledge_api_id="api-detail", tenant_id="tenant-1", session=session
)
def test_get_not_found(self, app: Flask): def test_get_not_found(self, app: Flask):
api = ExternalApiTemplateApi() api = ExternalApiTemplateApi()
method = inspect.unwrap(api.get) method = inspect.unwrap(api.get)
@ -114,6 +293,44 @@ class TestExternalApiTemplateApi:
with pytest.raises(NotFound): with pytest.raises(NotFound):
method(api, MagicMock(), "tenant-1", "api-id") method(api, MagicMock(), "tenant-1", "api-id")
def test_patch_success_uses_validated_payload_and_returns_template(self, app: Flask, current_user: Account):
api = ExternalApiTemplateApi()
method = inspect.unwrap(api.patch)
payload = {
"name": "Updated API",
"settings": {
"endpoint": "https://external.example.com/updated",
"api_key": "new-secret",
"headers": {"X-Version": "2"},
},
}
updated = _external_api_object("api-updated")
session = MagicMock()
with (
app.test_request_context("/", json=payload),
patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload),
patch.object(ExternalDatasetService, "validate_api_list") as validate_api_list,
patch.object(
ExternalDatasetService,
"update_external_knowledge_api",
return_value=updated,
) as update_external_knowledge_api,
):
resp, status = method(api, session, "tenant-1", current_user, "api-updated")
assert status == 200
assert resp == _external_api_dict("api-updated")
validate_api_list.assert_called_once_with(payload["settings"])
update_external_knowledge_api.assert_called_once_with(
tenant_id="tenant-1",
user_id="user-1",
external_knowledge_api_id="api-updated",
args=payload,
session=session,
)
def test_delete_forbidden(self, app: Flask, current_user: Account): def test_delete_forbidden(self, app: Flask, current_user: Account):
current_user.role = TenantAccountRole.NORMAL current_user.role = TenantAccountRole.NORMAL
@ -153,46 +370,39 @@ class TestExternalDatasetCreateApi:
method = inspect.unwrap(api.post) method = inspect.unwrap(api.post)
payload = { payload = {
"external_knowledge_api_id": "api", "external_knowledge_api_id": "api-1",
"external_knowledge_id": "kid", "external_knowledge_id": "knowledge-1",
"name": "dataset", "name": "Support knowledge",
"description": "External support articles",
"external_retrieval_model": {
"top_k": 4,
"score_threshold": 0.5,
"score_threshold_enabled": True,
},
} }
dataset = MagicMock() dataset = _dataset_detail_object()
dataset.embedding_available = False
dataset.built_in_field_enabled = False
dataset.is_published = False
dataset.enable_api = False
dataset.enable_qa = False
dataset.enable_vector_store = False
dataset.vector_store_setting = None
dataset.is_multimodal = False
dataset.retrieval_model_dict = {}
dataset.tags = []
dataset.external_knowledge_info = None
dataset.external_retrieval_model = None
dataset.doc_metadata = []
dataset.icon_info = None
dataset.permission_keys = []
dataset.summary_index_setting = MagicMock()
dataset.summary_index_setting.enable = False
with ( with (
app.test_request_context("/"), app.test_request_context("/", json=payload),
patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload), patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload),
patch.object( patch.object(
ExternalDatasetService, ExternalDatasetService,
"create_external_dataset", "create_external_dataset",
return_value=dataset, return_value=dataset,
), ) as create_external_dataset,
patch.object(external_module, "db", SimpleNamespace(session=lambda: MagicMock())),
): ):
_, status = method(api, MagicMock(), "tenant-1", current_user) session = MagicMock()
resp, status = method(api, session, "tenant-1", current_user)
assert status == 201 assert status == 201
assert resp == _expected_dataset_detail_payload()
create_external_dataset.assert_called_once_with(
tenant_id="tenant-1",
user_id="user-1",
args=payload,
session=session,
)
def test_create_forbidden(self, app: Flask, current_user: Account): def test_create_forbidden(self, app: Flask, current_user: Account):
current_user.role = TenantAccountRole.NORMAL current_user.role = TenantAccountRole.NORMAL
@ -233,24 +443,59 @@ class TestExternalKnowledgeHitTestingApi:
api = ExternalKnowledgeHitTestingApi() api = ExternalKnowledgeHitTestingApi()
method = inspect.unwrap(api.post) method = inspect.unwrap(api.post)
payload = {"query": "hello"} payload = {
"query": "hello",
"external_retrieval_model": {
"top_k": 3,
"score_threshold": 0.25,
"score_threshold_enabled": True,
},
"metadata_filtering_conditions": {
"logical_operator": "and",
"conditions": [{"name": "source", "comparison_operator": "contains", "value": "external"}],
},
}
dataset = MagicMock() dataset = MagicMock()
retrieve_response = {
"query": {"content": "hello"},
"records": [
{
"content": "answer",
"title": "doc",
"score": 0.9,
"metadata": {"source": "external", "page": 2},
}
],
}
session = MagicMock()
with ( with (
app.test_request_context("/"), app.test_request_context("/", json=payload),
patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload), patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload),
patch.object(DatasetService, "get_dataset", return_value=dataset), patch.object(DatasetService, "get_dataset", return_value=dataset),
patch.object(DatasetService, "check_dataset_permission"), patch.object(DatasetService, "check_dataset_permission") as check_dataset_permission,
patch.object(HitTestingService, "hit_testing_args_check") as hit_testing_args_check,
patch.object( patch.object(
HitTestingService, HitTestingService,
"external_retrieve", "external_retrieve",
return_value={"ok": True}, return_value=retrieve_response,
), ) as external_retrieve,
patch("controllers.console.datasets.external.dump_response", side_effect=lambda _model, value: value),
): ):
resp = method(api, MagicMock(), current_user, "dataset-id") resp = method(api, session, current_user, "dataset-id")
assert resp["ok"] is True assert resp == retrieve_response
check_dataset_permission.assert_called_once_with(dataset, current_user, session)
hit_testing_args_check.assert_called_once_with(payload)
external_retrieve.assert_called_once_with(
session=session,
dataset=dataset,
query="hello",
account=current_user,
external_retrieval_model=payload["external_retrieval_model"],
metadata_filtering_conditions=payload["metadata_filtering_conditions"],
)
class TestBedrockRetrievalApi: class TestBedrockRetrievalApi:
@ -259,24 +504,46 @@ class TestBedrockRetrievalApi:
method = inspect.unwrap(api.post) method = inspect.unwrap(api.post)
payload = { payload = {
"retrieval_setting": {}, "retrieval_setting": {"top_k": 5, "score_threshold": 0.72},
"query": "hello", "query": "hello bedrock",
"knowledge_id": "kid", "knowledge_id": "knowledge-base-1",
}
retrieval_response = {
"records": [
{
"metadata": {"source": "bedrock", "uri": "s3://bucket/doc.txt"},
"score": 0.8,
"title": "doc",
"content": "answer",
},
{
"metadata": {"source": "bedrock", "uri": "s3://bucket/other.txt"},
"score": 0.65,
"title": None,
"content": None,
},
]
} }
session = MagicMock()
with ( with (
app.test_request_context("/"), app.test_request_context("/", json=payload),
patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload), patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload),
patch.object( patch.object(
ExternalDatasetTestService, ExternalDatasetTestService,
"knowledge_retrieval", "knowledge_retrieval",
return_value={"ok": True}, return_value=retrieval_response,
), ) as knowledge_retrieval,
): ):
resp, status = method() resp, status = method()
assert status == 200 assert status == 200
assert resp["ok"] is True assert resp == retrieval_response
retrieval_setting, query, knowledge_id = knowledge_retrieval.call_args.args
assert retrieval_setting.model_dump() == payload["retrieval_setting"]
assert query == "hello bedrock"
assert knowledge_id == "knowledge-base-1"
class TestExternalApiTemplateListApiAdvanced: class TestExternalApiTemplateListApiAdvanced:
@ -302,10 +569,10 @@ class TestExternalApiTemplateListApiAdvanced:
api = ExternalApiTemplateListApi() api = ExternalApiTemplateListApi()
method = inspect.unwrap(api.get) method = inspect.unwrap(api.get)
templates = [MagicMock(id=f"api-{i}") for i in range(3)] templates = [_external_api_object(f"api-{i}") for i in range(3)]
with ( with (
app.test_request_context("/?page=1&limit=20"), app.test_request_context("/?page=2&limit=3"),
patch( patch(
"controllers.console.datasets.external.ExternalDatasetService.get_external_knowledge_apis", "controllers.console.datasets.external.ExternalDatasetService.get_external_knowledge_apis",
return_value=(templates, 25), return_value=(templates, 25),
@ -314,9 +581,14 @@ class TestExternalApiTemplateListApiAdvanced:
resp, status = method(api, "tenant-1") resp, status = method(api, "tenant-1")
assert status == 200 assert status == 200
assert resp["total"] == 25 assert resp == {
assert len(resp["data"]) == 3 "data": [_external_api_dict(f"api-{i}") for i in range(3)],
get_external_knowledge_apis.assert_called_once_with(1, 20, "tenant-1", None) "has_more": True,
"limit": 3,
"total": 25,
"page": 2,
}
get_external_knowledge_apis.assert_called_once_with(2, 3, "tenant-1", None)
class TestExternalDatasetCreateApiAdvanced: class TestExternalDatasetCreateApiAdvanced:
@ -371,6 +643,7 @@ class TestExternalKnowledgeHitTestingApiAdvanced:
"external_retrieval_model": {"type": "bm25"}, "external_retrieval_model": {"type": "bm25"},
"metadata_filtering_conditions": {"status": "active"}, "metadata_filtering_conditions": {"status": "active"},
} }
session = MagicMock()
with ( with (
app.test_request_context("/", json=payload), app.test_request_context("/", json=payload),
@ -379,15 +652,46 @@ class TestExternalKnowledgeHitTestingApiAdvanced:
"controllers.console.datasets.external.DatasetService.get_dataset", "controllers.console.datasets.external.DatasetService.get_dataset",
return_value=dataset, return_value=dataset,
), ),
patch("controllers.console.datasets.external.DatasetService.check_dataset_permission"), patch("controllers.console.datasets.external.DatasetService.check_dataset_permission") as check_permission,
patch("controllers.console.datasets.external.HitTestingService.hit_testing_args_check") as args_check,
patch( patch(
"controllers.console.datasets.external.HitTestingService.external_retrieve", "controllers.console.datasets.external.HitTestingService.external_retrieve",
return_value={"results": []}, return_value={
), "query": {"content": "test query"},
"records": [
{
"content": None,
"title": "metadata-only",
"score": None,
"metadata": {"status": "active"},
}
],
},
) as external_retrieve,
): ):
resp = method(api, MagicMock(), current_user, "ds-1") resp = method(api, session, current_user, "ds-1")
assert resp["results"] == [] assert resp == {
"query": {"content": "test query"},
"records": [
{
"content": None,
"title": "metadata-only",
"score": None,
"metadata": {"status": "active"},
}
],
}
check_permission.assert_called_once_with(dataset, current_user, session)
args_check.assert_called_once_with(payload)
external_retrieve.assert_called_once_with(
session=session,
dataset=dataset,
query="test query",
account=current_user,
external_retrieval_model={"type": "bm25"},
metadata_filtering_conditions={"status": "active"},
)
class TestBedrockRetrievalApiAdvanced: class TestBedrockRetrievalApiAdvanced:

View File

@ -359,8 +359,12 @@ export const get5 = oc
.input(z.object({ params: zGetDatasetsExternalKnowledgeApiByExternalKnowledgeApiIdPath })) .input(z.object({ params: zGetDatasetsExternalKnowledgeApiByExternalKnowledgeApiIdPath }))
.output(zGetDatasetsExternalKnowledgeApiByExternalKnowledgeApiIdResponse) .output(zGetDatasetsExternalKnowledgeApiByExternalKnowledgeApiIdResponse)
/**
* Update external knowledge API template
*/
export const patch = oc export const patch = oc
.route({ .route({
description: 'Update external knowledge API template',
inputStructure: 'detailed', inputStructure: 'detailed',
method: 'PATCH', method: 'PATCH',
operationId: 'patchDatasetsExternalKnowledgeApiByExternalKnowledgeApiId', operationId: 'patchDatasetsExternalKnowledgeApiByExternalKnowledgeApiId',
@ -397,8 +401,12 @@ export const get6 = oc
.input(z.object({ query: zGetDatasetsExternalKnowledgeApiQuery.optional() })) .input(z.object({ query: zGetDatasetsExternalKnowledgeApiQuery.optional() }))
.output(zGetDatasetsExternalKnowledgeApiResponse) .output(zGetDatasetsExternalKnowledgeApiResponse)
/**
* Create external knowledge API template
*/
export const post4 = oc export const post4 = oc
.route({ .route({
description: 'Create external knowledge API template',
inputStructure: 'detailed', inputStructure: 'detailed',
method: 'POST', method: 'POST',
operationId: 'postDatasetsExternalKnowledgeApi', operationId: 'postDatasetsExternalKnowledgeApi',
@ -444,7 +452,6 @@ export const post6 = oc
method: 'POST', method: 'POST',
operationId: 'postDatasetsInit', operationId: 'postDatasetsInit',
path: '/datasets/init', path: '/datasets/init',
successStatus: 201,
tags: ['console'], tags: ['console'],
}) })
.input(z.object({ body: zPostDatasetsInitBody })) .input(z.object({ body: zPostDatasetsInitBody }))
@ -1184,12 +1191,13 @@ export const segments = {
* - generating: Number of summaries being generated * - generating: Number of summaries being generated
* - error: Number of summaries with errors * - error: Number of summaries with errors
* - not_started: Number of segments without summary records * - not_started: Number of segments without summary records
* - timeout: Number of summaries that timed out
* - summaries: List of summary records with status and content preview * - summaries: List of summary records with status and content preview
*/ */
export const get22 = oc export const get22 = oc
.route({ .route({
description: description:
'Get summary index generation status for a document\nReturns:\n- total_segments: Total number of segments in the document\n- summary_status: Dictionary with status counts\n - completed: Number of summaries completed\n - generating: Number of summaries being generated\n - error: Number of summaries with errors\n - not_started: Number of segments without summary records\n- summaries: List of summary records with status and content preview', 'Get summary index generation status for a document\nReturns:\n- total_segments: Total number of segments in the document\n- summary_status: Dictionary with status counts\n - completed: Number of summaries completed\n - generating: Number of summaries being generated\n - error: Number of summaries with errors\n - not_started: Number of segments without summary records\n - timeout: Number of summaries that timed out\n- summaries: List of summary records with status and content preview',
inputStructure: 'detailed', inputStructure: 'detailed',
method: 'GET', method: 'GET',
operationId: 'getDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatus', operationId: 'getDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatus',

View File

@ -97,51 +97,12 @@ export type ExternalDatasetCreatePayload = {
name: string name: string
} }
export type DatasetDetail = {
app_count?: number
author_name?: string
built_in_field_enabled?: boolean
chunk_structure?: string
created_at?: number
created_by?: string
data_source_type?: string
description?: string
doc_form?: string
doc_metadata?: Array<DatasetDocMetadata>
document_count?: number
embedding_available?: boolean
embedding_model?: string
embedding_model_provider?: string
enable_api?: boolean
external_knowledge_info?: ExternalKnowledgeInfo
external_retrieval_model?: ExternalRetrievalModel
icon_info?: DatasetIconInfo
id?: string
indexing_technique?: string
is_multimodal?: boolean
is_published?: boolean
name?: string
permission?: string
permission_keys?: Array<string>
pipeline_id?: string
provider?: string
retrieval_model_dict?: DatasetRetrievalModel
runtime_mode?: string
summary_index_setting?: AnonymousInlineModelB1954337D565
tags?: Array<Tag>
total_available_documents?: number
total_documents?: number
updated_at?: number
updated_by?: string
word_count?: number
}
export type ExternalKnowledgeApiListResponse = { export type ExternalKnowledgeApiListResponse = {
data: Array<ExternalKnowledgeApiResponse> data: Array<ExternalKnowledgeApiResponse>
has_more: boolean has_more: boolean
limit: number limit: number
page: number page: number
total: number total: number | null
} }
export type ExternalKnowledgeApiPayload = { export type ExternalKnowledgeApiPayload = {
@ -154,11 +115,11 @@ export type ExternalKnowledgeApiPayload = {
export type ExternalKnowledgeApiResponse = { export type ExternalKnowledgeApiResponse = {
created_at: string created_at: string
created_by: string created_by: string
dataset_bindings?: Array<ExternalKnowledgeDatasetBindingResponse> dataset_bindings: Array<ExternalKnowledgeApiBindingResponse>
description: string description: string
id: string id: string
name: string name: string
settings?: { settings: {
[key: string]: unknown [key: string]: unknown
} | null } | null
tenant_id: string tenant_id: string
@ -183,8 +144,11 @@ export type IndexingEstimatePayload = {
} }
export type IndexingEstimateResponse = { export type IndexingEstimateResponse = {
preview: Array<IndexingEstimatePreviewItemResponse> currency: string
qa_preview?: Array<IndexingEstimateQaPreviewItemResponse> | null preview: Array<PreviewDetail>
qa_preview?: Array<QaPreviewDetail> | null
tokens: number
total_price: number | number
total_segments: number total_segments: number
} }
@ -236,8 +200,12 @@ export type IndexingEstimate = {
total_segments: number total_segments: number
} }
export type OpaqueObjectResponse = { export type ProcessRuleResponse = {
[key: string]: unknown limits: {
[key: string]: unknown
}
mode: ProcessRuleMode
rules?: Rule | null
} }
export type RetrievalSettingResponse = { export type RetrievalSettingResponse = {
@ -337,8 +305,6 @@ export type DocumentBatchDownloadZipPayload = {
document_ids: Array<string> document_ids: Array<string>
} }
export type BinaryFileResponse = Blob | File
export type GenerateSummaryPayload = { export type GenerateSummaryPayload = {
document_list: Array<string> document_list: Array<string>
} }
@ -347,6 +313,40 @@ export type MetadataOperationData = {
operation_data: Array<DocumentMetadataOperation> operation_data: Array<DocumentMetadataOperation>
} }
export type DocumentDetailResponse = {
archived?: boolean | null
average_segment_length?: number | null
completed_at?: number | null
created_at?: number | null
created_by?: string | null
created_from?: string | null
data_source_detail_dict?: unknown
data_source_info?: unknown
data_source_type?: string | null
dataset_process_rule?: unknown
dataset_process_rule_id?: string | null
disabled_at?: number | null
disabled_by?: string | null
display_status?: string | null
doc_form?: string | null
doc_language?: string | null
doc_metadata?: Array<DocumentMetadataResponse> | null
doc_type?: string | null
document_process_rule?: unknown
enabled?: boolean | null
error?: string | null
hit_count?: number | null
id: string
indexing_latency?: number | null
indexing_status?: string | null
name?: string | null
need_summary?: boolean | null
position?: number | null
segment_count?: number | null
tokens?: number | null
updated_at?: number | null
}
export type UrlResponse = { export type UrlResponse = {
url: string url: string
} }
@ -376,6 +376,13 @@ export type SimpleResultMessageResponse = {
result: string result: string
} }
export type DocumentPipelineExecutionLogResponse = {
datasource_info?: JsonValue | null
datasource_node_id?: string | null
datasource_type?: string | null
input_data?: JsonValue | null
}
export type DocumentRenamePayload = { export type DocumentRenamePayload = {
name: string name: string
} }
@ -464,6 +471,12 @@ export type ChildChunkUpdatePayload = {
content: string content: string
} }
export type DocumentSummaryStatusResponse = {
summaries: Array<SummaryEntryResponse>
summary_status: SummaryStatusResponse
total_segments: number
}
export type ErrorDocsResponse = { export type ErrorDocsResponse = {
data: Array<DocumentStatusResponse> data: Array<DocumentStatusResponse>
total: number total: number
@ -479,13 +492,10 @@ export type ExternalHitTestingPayload = {
query: string query: string
} }
export type ExternalRetrievalTestResponse export type ExternalHitTestingResponse = {
= | { query: ExternalHitTestingQueryResponse
[key: string]: unknown records: Array<ExternalHitTestingRecordResponse>
} }
| Array<{
[key: string]: unknown
}>
export type HitTestingPayload = { export type HitTestingPayload = {
attachment_ids?: Array<string> | null attachment_ids?: Array<string> | null
@ -641,68 +651,18 @@ export type DatasetTagResponse = {
type: string type: string
} }
export type DatasetDocMetadata = { export type ExternalKnowledgeApiBindingResponse = {
id?: string
name?: string
type?: string
}
export type ExternalKnowledgeInfo = {
external_knowledge_api_endpoint?: string
external_knowledge_api_id?: string
external_knowledge_api_name?: string
external_knowledge_id?: string
}
export type ExternalRetrievalModel = {
score_threshold?: number
score_threshold_enabled?: boolean
top_k?: number
}
export type DatasetIconInfo = {
icon?: string
icon_background?: string
icon_type?: string
icon_url?: string
}
export type DatasetRetrievalModel = {
reranking_enable?: boolean
reranking_mode?: string
reranking_model?: DatasetRerankingModel
score_threshold?: number
score_threshold_enabled?: boolean
search_method?: string
top_k?: number
weights?: DatasetWeightedScore
}
export type AnonymousInlineModelB1954337D565 = {
enable?: boolean
model_name?: string
model_provider_name?: string
summary_prompt?: string
}
export type Tag = {
id: string
name: string
type: string
}
export type ExternalKnowledgeDatasetBindingResponse = {
id: string id: string
name: string name: string
} }
export type IndexingEstimatePreviewItemResponse = { export type PreviewDetail = {
child_chunks?: Array<string> | null child_chunks?: Array<string> | null
content: string content: string
summary?: string | null summary?: string | null
} }
export type IndexingEstimateQaPreviewItemResponse = { export type QaPreviewDetail = {
answer: string answer: string
question: string question: string
} }
@ -744,15 +704,13 @@ export type DatasetMetadataBuiltInFieldResponse = {
type: string type: string
} }
export type PreviewDetail = { export type ProcessRuleMode = 'automatic' | 'custom' | 'hierarchical'
child_chunks?: Array<string> | null
content: string
summary?: string | null
}
export type QaPreviewDetail = { export type Rule = {
answer: string parent_mode?: 'full-doc' | 'paragraph' | null
question: string pre_processing_rules?: Array<PreProcessingRule> | null
segmentation?: Segmentation | null
subchunk_segmentation?: Segmentation | null
} }
export type DocumentWithSegmentsResponse = { export type DocumentWithSegmentsResponse = {
@ -798,6 +756,8 @@ export type DocumentMetadataResponse = {
value?: string | number | number | boolean | null value?: string | number | number | boolean | null
} }
export type JsonValue = unknown
export type SegmentResponse = { export type SegmentResponse = {
answer: string | null answer: string | null
attachments: Array<SegmentAttachmentResponse> attachments: Array<SegmentAttachmentResponse>
@ -844,6 +804,37 @@ export type ChildChunkUpdateArgs = {
id?: string | null id?: string | null
} }
export type SummaryEntryResponse = {
created_at?: number | null
error?: string | null
segment_id: string
segment_position: number
status: string
summary_preview?: string | null
updated_at?: number | null
}
export type SummaryStatusResponse = {
completed?: number
error?: number
generating?: number
not_started?: number
timeout?: number
}
export type ExternalHitTestingQueryResponse = {
content: string
}
export type ExternalHitTestingRecordResponse = {
content?: string | null
metadata?: {
[key: string]: unknown
} | null
score?: number | null
title?: string | null
}
export type HitTestingQuery = { export type HitTestingQuery = {
content: string content: string
} }
@ -896,17 +887,6 @@ export type DatasetWeightedScoreResponse = {
weight_type?: string | null weight_type?: string | null
} }
export type DatasetRerankingModel = {
reranking_model_name?: string
reranking_provider_name?: string
}
export type DatasetWeightedScore = {
keyword_setting?: DatasetKeywordSetting
vector_setting?: DatasetVectorSetting
weight_type?: string
}
export type InfoList = { export type InfoList = {
data_source_type: 'notion_import' | 'upload_file' | 'website_crawl' data_source_type: 'notion_import' | 'upload_file' | 'website_crawl'
file_info_list?: FileInfo | null file_info_list?: FileInfo | null
@ -914,15 +894,6 @@ export type InfoList = {
website_info_list?: WebsiteInfo | null website_info_list?: WebsiteInfo | null
} }
export type ProcessRuleMode = 'automatic' | 'custom' | 'hierarchical'
export type Rule = {
parent_mode?: 'full-doc' | 'paragraph' | null
pre_processing_rules?: Array<PreProcessingRule> | null
segmentation?: Segmentation | null
subchunk_segmentation?: Segmentation | null
}
export type MetadataFilteringCondition = { export type MetadataFilteringCondition = {
conditions?: Array<Condition> | null conditions?: Array<Condition> | null
logical_operator?: 'and' | 'or' | null logical_operator?: 'and' | 'or' | null
@ -945,6 +916,17 @@ export type WeightModel = {
weight_type?: 'customized' | 'keyword_first' | 'semantic_first' | null weight_type?: 'customized' | 'keyword_first' | 'semantic_first' | null
} }
export type PreProcessingRule = {
enabled: boolean
id: 'remove_extra_spaces' | 'remove_stopwords' | 'remove_urls_emails'
}
export type Segmentation = {
chunk_overlap?: number
max_tokens: number
separator?: string
}
export type MetadataDetail = { export type MetadataDetail = {
id: string id: string
name: string name: string
@ -1018,16 +1000,6 @@ export type DatasetVectorSettingResponse = {
vector_weight?: number | null vector_weight?: number | null
} }
export type DatasetKeywordSetting = {
keyword_weight?: number
}
export type DatasetVectorSetting = {
embedding_model_name?: string
embedding_provider_name?: string
vector_weight?: number
}
export type FileInfo = { export type FileInfo = {
file_ids: Array<string> file_ids: Array<string>
} }
@ -1045,17 +1017,6 @@ export type WebsiteInfo = {
urls: Array<string> urls: Array<string>
} }
export type PreProcessingRule = {
enabled: boolean
id: 'remove_extra_spaces' | 'remove_stopwords' | 'remove_urls_emails'
}
export type Segmentation = {
chunk_overlap?: number
max_tokens: number
separator?: string
}
export type Condition = { export type Condition = {
comparison_operator: comparison_operator:
| '<' | '<'
@ -1264,7 +1225,7 @@ export type PostDatasetsExternalErrors = {
} }
export type PostDatasetsExternalResponses = { export type PostDatasetsExternalResponses = {
201: DatasetDetail 201: DatasetDetailResponse
} }
export type PostDatasetsExternalResponse export type PostDatasetsExternalResponse
@ -1295,6 +1256,10 @@ export type PostDatasetsExternalKnowledgeApiData = {
url: '/datasets/external-knowledge-api' url: '/datasets/external-knowledge-api'
} }
export type PostDatasetsExternalKnowledgeApiErrors = {
403: unknown
}
export type PostDatasetsExternalKnowledgeApiResponses = { export type PostDatasetsExternalKnowledgeApiResponses = {
201: ExternalKnowledgeApiResponse 201: ExternalKnowledgeApiResponse
} }
@ -1347,6 +1312,10 @@ export type PatchDatasetsExternalKnowledgeApiByExternalKnowledgeApiIdData = {
url: '/datasets/external-knowledge-api/{external_knowledge_api_id}' url: '/datasets/external-knowledge-api/{external_knowledge_api_id}'
} }
export type PatchDatasetsExternalKnowledgeApiByExternalKnowledgeApiIdErrors = {
404: unknown
}
export type PatchDatasetsExternalKnowledgeApiByExternalKnowledgeApiIdResponses = { export type PatchDatasetsExternalKnowledgeApiByExternalKnowledgeApiIdResponses = {
200: ExternalKnowledgeApiResponse 200: ExternalKnowledgeApiResponse
} }
@ -1396,7 +1365,7 @@ export type PostDatasetsInitErrors = {
} }
export type PostDatasetsInitResponses = { export type PostDatasetsInitResponses = {
201: DatasetAndDocumentResponse 200: DatasetAndDocumentResponse
} }
export type PostDatasetsInitResponse = PostDatasetsInitResponses[keyof PostDatasetsInitResponses] export type PostDatasetsInitResponse = PostDatasetsInitResponses[keyof PostDatasetsInitResponses]
@ -1439,7 +1408,7 @@ export type GetDatasetsProcessRuleData = {
} }
export type GetDatasetsProcessRuleResponses = { export type GetDatasetsProcessRuleResponses = {
200: OpaqueObjectResponse 200: ProcessRuleResponse
} }
export type GetDatasetsProcessRuleResponse export type GetDatasetsProcessRuleResponse
@ -1581,7 +1550,7 @@ export type GetDatasetsByDatasetIdBatchByBatchIndexingEstimateData = {
} }
export type GetDatasetsByDatasetIdBatchByBatchIndexingEstimateResponses = { export type GetDatasetsByDatasetIdBatchByBatchIndexingEstimateResponses = {
200: OpaqueObjectResponse 200: IndexingEstimateResponse
} }
export type GetDatasetsByDatasetIdBatchByBatchIndexingEstimateResponse export type GetDatasetsByDatasetIdBatchByBatchIndexingEstimateResponse
@ -1669,7 +1638,9 @@ export type PostDatasetsByDatasetIdDocumentsDownloadZipData = {
} }
export type PostDatasetsByDatasetIdDocumentsDownloadZipResponses = { export type PostDatasetsByDatasetIdDocumentsDownloadZipResponses = {
200: BinaryFileResponse 200: {
[key: string]: unknown
}
} }
export type PostDatasetsByDatasetIdDocumentsDownloadZipResponse export type PostDatasetsByDatasetIdDocumentsDownloadZipResponse
@ -1764,7 +1735,7 @@ export type GetDatasetsByDatasetIdDocumentsByDocumentIdErrors = {
} }
export type GetDatasetsByDatasetIdDocumentsByDocumentIdResponses = { export type GetDatasetsByDatasetIdDocumentsByDocumentIdResponses = {
200: OpaqueObjectResponse 200: DocumentDetailResponse
} }
export type GetDatasetsByDatasetIdDocumentsByDocumentIdResponse export type GetDatasetsByDatasetIdDocumentsByDocumentIdResponse
@ -1803,7 +1774,7 @@ export type GetDatasetsByDatasetIdDocumentsByDocumentIdIndexingEstimateErrors =
} }
export type GetDatasetsByDatasetIdDocumentsByDocumentIdIndexingEstimateResponses = { export type GetDatasetsByDatasetIdDocumentsByDocumentIdIndexingEstimateResponses = {
200: OpaqueObjectResponse 200: IndexingEstimateResponse
} }
export type GetDatasetsByDatasetIdDocumentsByDocumentIdIndexingEstimateResponse export type GetDatasetsByDatasetIdDocumentsByDocumentIdIndexingEstimateResponse
@ -1880,7 +1851,7 @@ export type GetDatasetsByDatasetIdDocumentsByDocumentIdPipelineExecutionLogData
} }
export type GetDatasetsByDatasetIdDocumentsByDocumentIdPipelineExecutionLogResponses = { export type GetDatasetsByDatasetIdDocumentsByDocumentIdPipelineExecutionLogResponses = {
200: OpaqueObjectResponse 200: DocumentPipelineExecutionLogResponse
} }
export type GetDatasetsByDatasetIdDocumentsByDocumentIdPipelineExecutionLogResponse export type GetDatasetsByDatasetIdDocumentsByDocumentIdPipelineExecutionLogResponse
@ -2225,7 +2196,7 @@ export type GetDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatusErrors = {
} }
export type GetDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatusResponses = { export type GetDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatusResponses = {
200: OpaqueObjectResponse 200: DocumentSummaryStatusResponse
} }
export type GetDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatusResponse export type GetDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatusResponse
@ -2283,7 +2254,7 @@ export type PostDatasetsByDatasetIdExternalHitTestingErrors = {
} }
export type PostDatasetsByDatasetIdExternalHitTestingResponses = { export type PostDatasetsByDatasetIdExternalHitTestingResponses = {
200: ExternalRetrievalTestResponse 200: ExternalHitTestingResponse
} }
export type PostDatasetsByDatasetIdExternalHitTestingResponse export type PostDatasetsByDatasetIdExternalHitTestingResponse

View File

@ -91,11 +91,6 @@ export const zNotionEstimatePayload = z.object({
process_rule: z.record(z.string(), z.unknown()), process_rule: z.record(z.string(), z.unknown()),
}) })
/**
* OpaqueObjectResponse
*/
export const zOpaqueObjectResponse = z.record(z.string(), z.unknown())
/** /**
* RetrievalSettingResponse * RetrievalSettingResponse
*/ */
@ -127,11 +122,6 @@ export const zDocumentBatchDownloadZipPayload = z.object({
document_ids: z.array(z.uuid()).min(1).max(100), document_ids: z.array(z.uuid()).min(1).max(100),
}) })
/**
* BinaryFileResponse
*/
export const zBinaryFileResponse = z.custom<Blob | File>()
/** /**
* GenerateSummaryPayload * GenerateSummaryPayload
*/ */
@ -247,14 +237,6 @@ export const zExternalHitTestingPayload = z.object({
query: z.string(), query: z.string(),
}) })
/**
* ExternalRetrievalTestResponse
*/
export const zExternalRetrievalTestResponse = z.union([
z.record(z.string(), z.unknown()),
z.array(z.record(z.string(), z.unknown())),
])
/** /**
* MetadataArgs * MetadataArgs
*/ */
@ -397,52 +379,10 @@ export const zDatasetTagResponse = z.object({
type: z.string(), type: z.string(),
}) })
export const zDatasetDocMetadata = z.object({
id: z.string().optional(),
name: z.string().optional(),
type: z.string().optional(),
})
export const zExternalKnowledgeInfo = z.object({
external_knowledge_api_endpoint: z.string().optional(),
external_knowledge_api_id: z.string().optional(),
external_knowledge_api_name: z.string().optional(),
external_knowledge_id: z.string().optional(),
})
export const zExternalRetrievalModel = z.object({
score_threshold: z.number().optional(),
score_threshold_enabled: z.boolean().optional(),
top_k: z.int().optional(),
})
export const zDatasetIconInfo = z.object({
icon: z.string().optional(),
icon_background: z.string().optional(),
icon_type: z.string().optional(),
icon_url: z.string().optional(),
})
export const zAnonymousInlineModelB1954337D565 = z.object({
enable: z.boolean().optional(),
model_name: z.string().optional(),
model_provider_name: z.string().optional(),
summary_prompt: z.string().optional(),
})
/** /**
* Tag * ExternalKnowledgeApiBindingResponse
*/ */
export const zTag = z.object({ export const zExternalKnowledgeApiBindingResponse = z.object({
id: z.string(),
name: z.string(),
type: z.string(),
})
/**
* ExternalKnowledgeDatasetBindingResponse
*/
export const zExternalKnowledgeDatasetBindingResponse = z.object({
id: z.string(), id: z.string(),
name: z.string(), name: z.string(),
}) })
@ -453,11 +393,11 @@ export const zExternalKnowledgeDatasetBindingResponse = z.object({
export const zExternalKnowledgeApiResponse = z.object({ export const zExternalKnowledgeApiResponse = z.object({
created_at: z.string(), created_at: z.string(),
created_by: z.string(), created_by: z.string(),
dataset_bindings: z.array(zExternalKnowledgeDatasetBindingResponse).optional(), dataset_bindings: z.array(zExternalKnowledgeApiBindingResponse),
description: z.string(), description: z.string(),
id: z.string(), id: z.string(),
name: z.string(), name: z.string(),
settings: z.record(z.string(), z.unknown()).nullish(), settings: z.record(z.string(), z.unknown()).nullable(),
tenant_id: z.string(), tenant_id: z.string(),
}) })
@ -469,22 +409,22 @@ export const zExternalKnowledgeApiListResponse = z.object({
has_more: z.boolean(), has_more: z.boolean(),
limit: z.int(), limit: z.int(),
page: z.int(), page: z.int(),
total: z.int(), total: z.int().nullable(),
}) })
/** /**
* IndexingEstimatePreviewItemResponse * PreviewDetail
*/ */
export const zIndexingEstimatePreviewItemResponse = z.object({ export const zPreviewDetail = z.object({
child_chunks: z.array(z.string()).nullish(), child_chunks: z.array(z.string()).nullish(),
content: z.string(), content: z.string(),
summary: z.string().nullish(), summary: z.string().nullish(),
}) })
/** /**
* IndexingEstimateQaPreviewItemResponse * QAPreviewDetail
*/ */
export const zIndexingEstimateQaPreviewItemResponse = z.object({ export const zQaPreviewDetail = z.object({
answer: z.string(), answer: z.string(),
question: z.string(), question: z.string(),
}) })
@ -493,8 +433,20 @@ export const zIndexingEstimateQaPreviewItemResponse = z.object({
* IndexingEstimateResponse * IndexingEstimateResponse
*/ */
export const zIndexingEstimateResponse = z.object({ export const zIndexingEstimateResponse = z.object({
preview: z.array(zIndexingEstimatePreviewItemResponse), currency: z.string(),
qa_preview: z.array(zIndexingEstimateQaPreviewItemResponse).nullish(), preview: z.array(zPreviewDetail),
qa_preview: z.array(zQaPreviewDetail).nullish(),
tokens: z.int(),
total_price: z.union([z.number(), z.int()]),
total_segments: z.int(),
})
/**
* IndexingEstimate
*/
export const zIndexingEstimate = z.object({
preview: z.array(zPreviewDetail),
qa_preview: z.array(zQaPreviewDetail).nullish(),
total_segments: z.int(), total_segments: z.int(),
}) })
@ -528,30 +480,11 @@ export const zDatasetMetadataBuiltInFieldsResponse = z.object({
}) })
/** /**
* PreviewDetail * ProcessRuleMode
*
* Dataset Process Rule Mode
*/ */
export const zPreviewDetail = z.object({ export const zProcessRuleMode = z.enum(['automatic', 'custom', 'hierarchical'])
child_chunks: z.array(z.string()).nullish(),
content: z.string(),
summary: z.string().nullish(),
})
/**
* QAPreviewDetail
*/
export const zQaPreviewDetail = z.object({
answer: z.string(),
question: z.string(),
})
/**
* IndexingEstimate
*/
export const zIndexingEstimate = z.object({
preview: z.array(zPreviewDetail),
qa_preview: z.array(zQaPreviewDetail).nullish(),
total_segments: z.int(),
})
/** /**
* DocumentMetadataResponse * DocumentMetadataResponse
@ -563,6 +496,43 @@ export const zDocumentMetadataResponse = z.object({
value: z.union([z.string(), z.int(), z.number(), z.boolean()]).nullish(), value: z.union([z.string(), z.int(), z.number(), z.boolean()]).nullish(),
}) })
/**
* DocumentDetailResponse
*/
export const zDocumentDetailResponse = z.object({
archived: z.boolean().nullish(),
average_segment_length: z.number().nullish(),
completed_at: z.int().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
created_from: z.string().nullish(),
data_source_detail_dict: z.unknown().optional(),
data_source_info: z.unknown().optional(),
data_source_type: z.string().nullish(),
dataset_process_rule: z.unknown().optional(),
dataset_process_rule_id: z.string().nullish(),
disabled_at: z.int().nullish(),
disabled_by: z.string().nullish(),
display_status: z.string().nullish(),
doc_form: z.string().nullish(),
doc_language: z.string().nullish(),
doc_metadata: z.array(zDocumentMetadataResponse).nullish(),
doc_type: z.string().nullish(),
document_process_rule: z.unknown().optional(),
enabled: z.boolean().nullish(),
error: z.string().nullish(),
hit_count: z.int().nullish(),
id: z.string(),
indexing_latency: z.number().nullish(),
indexing_status: z.string().nullish(),
name: z.string().nullish(),
need_summary: z.boolean().nullish(),
position: z.int().nullish(),
segment_count: z.int().nullish(),
tokens: z.int().nullish(),
updated_at: z.int().nullish(),
})
/** /**
* DocumentResponse * DocumentResponse
*/ */
@ -646,6 +616,18 @@ export const zDocumentWithSegmentsListResponse = z.object({
total: z.int(), total: z.int(),
}) })
export const zJsonValue = z.unknown()
/**
* DocumentPipelineExecutionLogResponse
*/
export const zDocumentPipelineExecutionLogResponse = z.object({
datasource_info: zJsonValue.nullish(),
datasource_node_id: z.string().nullish(),
datasource_type: z.string().nullish(),
input_data: zJsonValue.nullish(),
})
/** /**
* ChildChunkResponse * ChildChunkResponse
*/ */
@ -700,6 +682,64 @@ export const zChildChunkBatchUpdatePayload = z.object({
chunks: z.array(zChildChunkUpdateArgs), chunks: z.array(zChildChunkUpdateArgs),
}) })
/**
* SummaryEntryResponse
*/
export const zSummaryEntryResponse = z.object({
created_at: z.int().nullish(),
error: z.string().nullish(),
segment_id: z.string(),
segment_position: z.int(),
status: z.string(),
summary_preview: z.string().nullish(),
updated_at: z.int().nullish(),
})
/**
* SummaryStatusResponse
*/
export const zSummaryStatusResponse = z.object({
completed: z.int().optional().default(0),
error: z.int().optional().default(0),
generating: z.int().optional().default(0),
not_started: z.int().optional().default(0),
timeout: z.int().optional().default(0),
})
/**
* DocumentSummaryStatusResponse
*/
export const zDocumentSummaryStatusResponse = z.object({
summaries: z.array(zSummaryEntryResponse),
summary_status: zSummaryStatusResponse,
total_segments: z.int(),
})
/**
* ExternalHitTestingQueryResponse
*/
export const zExternalHitTestingQueryResponse = z.object({
content: z.string(),
})
/**
* ExternalHitTestingRecordResponse
*/
export const zExternalHitTestingRecordResponse = z.object({
content: z.string().nullish(),
metadata: z.record(z.string(), z.unknown()).nullish(),
score: z.number().nullish(),
title: z.string().nullish(),
})
/**
* ExternalHitTestingResponse
*/
export const zExternalHitTestingResponse = z.object({
query: zExternalHitTestingQueryResponse,
records: z.array(zExternalHitTestingRecordResponse),
})
/** /**
* HitTestingQuery * HitTestingQuery
*/ */
@ -755,18 +795,6 @@ export const zDatasetRerankingModelResponse = z.object({
reranking_provider_name: z.string().nullish(), reranking_provider_name: z.string().nullish(),
}) })
export const zDatasetRerankingModel = z.object({
reranking_model_name: z.string().optional(),
reranking_provider_name: z.string().optional(),
})
/**
* ProcessRuleMode
*
* Dataset Process Rule Mode
*/
export const zProcessRuleMode = z.enum(['automatic', 'custom', 'hierarchical'])
/** /**
* RerankingModel * RerankingModel
*/ */
@ -785,6 +813,50 @@ export const zRetrievalMethod = z.enum([
'semantic_search', 'semantic_search',
]) ])
/**
* PreProcessingRule
*/
export const zPreProcessingRule = z.object({
enabled: z.boolean(),
id: z.enum(['remove_extra_spaces', 'remove_stopwords', 'remove_urls_emails']),
})
/**
* Segmentation
*/
export const zSegmentation = z.object({
chunk_overlap: z.int().optional().default(0),
max_tokens: z.int(),
separator: z.string().optional().default('\n'),
})
/**
* Rule
*/
export const zRule = z.object({
parent_mode: z.enum(['full-doc', 'paragraph']).nullish(),
pre_processing_rules: z.array(zPreProcessingRule).nullish(),
segmentation: zSegmentation.nullish(),
subchunk_segmentation: zSegmentation.nullish(),
})
/**
* ProcessRuleResponse
*/
export const zProcessRuleResponse = z.object({
limits: z.record(z.string(), z.unknown()),
mode: zProcessRuleMode,
rules: zRule.nullish(),
})
/**
* ProcessRule
*/
export const zProcessRule = z.object({
mode: zProcessRuleMode,
rules: zRule.nullish(),
})
/** /**
* MetadataDetail * MetadataDetail
*/ */
@ -1079,88 +1151,6 @@ export const zDatasetListResponse = z.object({
total: z.int(), total: z.int(),
}) })
export const zDatasetKeywordSetting = z.object({
keyword_weight: z.number().optional(),
})
export const zDatasetVectorSetting = z.object({
embedding_model_name: z.string().optional(),
embedding_provider_name: z.string().optional(),
vector_weight: z.number().optional(),
})
export const zDatasetWeightedScore = z.object({
keyword_setting: zDatasetKeywordSetting.optional(),
vector_setting: zDatasetVectorSetting.optional(),
weight_type: z.string().optional(),
})
export const zDatasetRetrievalModel = z.object({
reranking_enable: z.boolean().optional(),
reranking_mode: z.string().optional(),
reranking_model: zDatasetRerankingModel.optional(),
score_threshold: z.number().optional(),
score_threshold_enabled: z.boolean().optional(),
search_method: z.string().optional(),
top_k: z.int().optional(),
weights: zDatasetWeightedScore.optional(),
})
export const zDatasetDetail = z.object({
app_count: z.int().optional(),
author_name: z.string().optional(),
built_in_field_enabled: z.boolean().optional(),
chunk_structure: z.string().optional(),
created_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
created_by: z.string().optional(),
data_source_type: z.string().optional(),
description: z.string().optional(),
doc_form: z.string().optional(),
doc_metadata: z.array(zDatasetDocMetadata).optional(),
document_count: z.int().optional(),
embedding_available: z.boolean().optional(),
embedding_model: z.string().optional(),
embedding_model_provider: z.string().optional(),
enable_api: z.boolean().optional(),
external_knowledge_info: zExternalKnowledgeInfo.optional(),
external_retrieval_model: zExternalRetrievalModel.optional(),
icon_info: zDatasetIconInfo.optional(),
id: z.string().optional(),
indexing_technique: z.string().optional(),
is_multimodal: z.boolean().optional(),
is_published: z.boolean().optional(),
name: z.string().optional(),
permission: z.string().optional(),
permission_keys: z.array(z.string()).optional(),
pipeline_id: z.string().optional(),
provider: z.string().optional(),
retrieval_model_dict: zDatasetRetrievalModel.optional(),
runtime_mode: z.string().optional(),
summary_index_setting: zAnonymousInlineModelB1954337D565.optional(),
tags: z.array(zTag).optional(),
total_available_documents: z.int().optional(),
total_documents: z.int().optional(),
updated_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
updated_by: z.string().optional(),
word_count: z.int().optional(),
})
/** /**
* FileInfo * FileInfo
*/ */
@ -1178,41 +1168,6 @@ export const zWebsiteInfo = z.object({
urls: z.array(z.string()), urls: z.array(z.string()),
}) })
/**
* PreProcessingRule
*/
export const zPreProcessingRule = z.object({
enabled: z.boolean(),
id: z.enum(['remove_extra_spaces', 'remove_stopwords', 'remove_urls_emails']),
})
/**
* Segmentation
*/
export const zSegmentation = z.object({
chunk_overlap: z.int().optional().default(0),
max_tokens: z.int(),
separator: z.string().optional().default('\n'),
})
/**
* Rule
*/
export const zRule = z.object({
parent_mode: z.enum(['full-doc', 'paragraph']).nullish(),
pre_processing_rules: z.array(zPreProcessingRule).nullish(),
segmentation: zSegmentation.nullish(),
subchunk_segmentation: zSegmentation.nullish(),
})
/**
* ProcessRule
*/
export const zProcessRule = z.object({
mode: zProcessRuleMode,
rules: zRule.nullish(),
})
/** /**
* Condition * Condition
* *
@ -1558,7 +1513,7 @@ export const zPostDatasetsExternalBody = zExternalDatasetCreatePayload
/** /**
* External dataset created successfully * External dataset created successfully
*/ */
export const zPostDatasetsExternalResponse = zDatasetDetail export const zPostDatasetsExternalResponse = zDatasetDetailResponse
export const zGetDatasetsExternalKnowledgeApiQuery = z.object({ export const zGetDatasetsExternalKnowledgeApiQuery = z.object({
keyword: z.string().optional(), keyword: z.string().optional(),
@ -1653,7 +1608,7 @@ export const zGetDatasetsProcessRuleQuery = z.object({
/** /**
* Process rules retrieved successfully * Process rules retrieved successfully
*/ */
export const zGetDatasetsProcessRuleResponse = zOpaqueObjectResponse export const zGetDatasetsProcessRuleResponse = zProcessRuleResponse
/** /**
* Retrieval settings retrieved successfully * Retrieval settings retrieved successfully
@ -1723,9 +1678,9 @@ export const zGetDatasetsByDatasetIdBatchByBatchIndexingEstimatePath = z.object(
}) })
/** /**
* Batch indexing estimate calculated successfully * Indexing estimate calculated successfully
*/ */
export const zGetDatasetsByDatasetIdBatchByBatchIndexingEstimateResponse = zOpaqueObjectResponse export const zGetDatasetsByDatasetIdBatchByBatchIndexingEstimateResponse = zIndexingEstimateResponse
export const zGetDatasetsByDatasetIdBatchByBatchIndexingStatusPath = z.object({ export const zGetDatasetsByDatasetIdBatchByBatchIndexingStatusPath = z.object({
batch: z.string(), batch: z.string(),
@ -1782,9 +1737,12 @@ export const zPostDatasetsByDatasetIdDocumentsDownloadZipPath = z.object({
}) })
/** /**
* ZIP archive generated successfully * ZIP archive downloaded successfully
*/ */
export const zPostDatasetsByDatasetIdDocumentsDownloadZipResponse = zBinaryFileResponse export const zPostDatasetsByDatasetIdDocumentsDownloadZipResponse = z.record(
z.string(),
z.unknown(),
)
export const zPostDatasetsByDatasetIdDocumentsGenerateSummaryBody = zGenerateSummaryPayload export const zPostDatasetsByDatasetIdDocumentsGenerateSummaryBody = zGenerateSummaryPayload
@ -1840,7 +1798,7 @@ export const zGetDatasetsByDatasetIdDocumentsByDocumentIdQuery = z.object({
/** /**
* Document retrieved successfully * Document retrieved successfully
*/ */
export const zGetDatasetsByDatasetIdDocumentsByDocumentIdResponse = zOpaqueObjectResponse export const zGetDatasetsByDatasetIdDocumentsByDocumentIdResponse = zDocumentDetailResponse
export const zGetDatasetsByDatasetIdDocumentsByDocumentIdDownloadPath = z.object({ export const zGetDatasetsByDatasetIdDocumentsByDocumentIdDownloadPath = z.object({
dataset_id: z.uuid(), dataset_id: z.uuid(),
@ -1861,7 +1819,7 @@ export const zGetDatasetsByDatasetIdDocumentsByDocumentIdIndexingEstimatePath =
* Indexing estimate calculated successfully * Indexing estimate calculated successfully
*/ */
export const zGetDatasetsByDatasetIdDocumentsByDocumentIdIndexingEstimateResponse export const zGetDatasetsByDatasetIdDocumentsByDocumentIdIndexingEstimateResponse
= zOpaqueObjectResponse = zIndexingEstimateResponse
export const zGetDatasetsByDatasetIdDocumentsByDocumentIdIndexingStatusPath = z.object({ export const zGetDatasetsByDatasetIdDocumentsByDocumentIdIndexingStatusPath = z.object({
dataset_id: z.uuid(), dataset_id: z.uuid(),
@ -1904,10 +1862,10 @@ export const zGetDatasetsByDatasetIdDocumentsByDocumentIdPipelineExecutionLogPat
}) })
/** /**
* Document pipeline execution log retrieved successfully * Pipeline execution log retrieved successfully
*/ */
export const zGetDatasetsByDatasetIdDocumentsByDocumentIdPipelineExecutionLogResponse export const zGetDatasetsByDatasetIdDocumentsByDocumentIdPipelineExecutionLogResponse
= zOpaqueObjectResponse = zDocumentPipelineExecutionLogResponse
export const zPatchDatasetsByDatasetIdDocumentsByDocumentIdProcessingPausePath = z.object({ export const zPatchDatasetsByDatasetIdDocumentsByDocumentIdProcessingPausePath = z.object({
dataset_id: z.uuid(), dataset_id: z.uuid(),
@ -2158,7 +2116,7 @@ export const zGetDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatusPath = z.o
* Summary status retrieved successfully * Summary status retrieved successfully
*/ */
export const zGetDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatusResponse export const zGetDatasetsByDatasetIdDocumentsByDocumentIdSummaryStatusResponse
= zOpaqueObjectResponse = zDocumentSummaryStatusResponse
export const zGetDatasetsByDatasetIdDocumentsByDocumentIdWebsiteSyncPath = z.object({ export const zGetDatasetsByDatasetIdDocumentsByDocumentIdWebsiteSyncPath = z.object({
dataset_id: z.uuid(), dataset_id: z.uuid(),
@ -2188,7 +2146,7 @@ export const zPostDatasetsByDatasetIdExternalHitTestingPath = z.object({
/** /**
* External hit testing completed successfully * External hit testing completed successfully
*/ */
export const zPostDatasetsByDatasetIdExternalHitTestingResponse = zExternalRetrievalTestResponse export const zPostDatasetsByDatasetIdExternalHitTestingResponse = zExternalHitTestingResponse
export const zPostDatasetsByDatasetIdHitTestingBody = zHitTestingPayload export const zPostDatasetsByDatasetIdHitTestingBody = zHitTestingPayload

View File

@ -10,19 +10,24 @@ export type BedrockRetrievalPayload = {
retrieval_setting: BedrockRetrievalSetting retrieval_setting: BedrockRetrievalSetting
} }
export type ExternalRetrievalTestResponse export type BedrockRetrievalResponse = {
= | { records: Array<BedrockRetrievalRecordResponse>
[key: string]: unknown }
}
| Array<{
[key: string]: unknown
}>
export type BedrockRetrievalSetting = { export type BedrockRetrievalSetting = {
score_threshold?: number score_threshold?: number
top_k?: number | null top_k?: number | null
} }
export type BedrockRetrievalRecordResponse = {
content?: string | null
metadata?: {
[key: string]: unknown
} | null
score: number
title?: string | null
}
export type PostTestRetrievalData = { export type PostTestRetrievalData = {
body: BedrockRetrievalPayload body: BedrockRetrievalPayload
path?: never path?: never
@ -31,7 +36,7 @@ export type PostTestRetrievalData = {
} }
export type PostTestRetrievalResponses = { export type PostTestRetrievalResponses = {
200: ExternalRetrievalTestResponse 200: BedrockRetrievalResponse
} }
export type PostTestRetrievalResponse = PostTestRetrievalResponses[keyof PostTestRetrievalResponses] export type PostTestRetrievalResponse = PostTestRetrievalResponses[keyof PostTestRetrievalResponses]

View File

@ -2,14 +2,6 @@
import * as z from 'zod' import * as z from 'zod'
/**
* ExternalRetrievalTestResponse
*/
export const zExternalRetrievalTestResponse = z.union([
z.record(z.string(), z.unknown()),
z.array(z.record(z.string(), z.unknown())),
])
/** /**
* BedrockRetrievalSetting * BedrockRetrievalSetting
* *
@ -29,9 +21,26 @@ export const zBedrockRetrievalPayload = z.object({
retrieval_setting: zBedrockRetrievalSetting, retrieval_setting: zBedrockRetrievalSetting,
}) })
/**
* BedrockRetrievalRecordResponse
*/
export const zBedrockRetrievalRecordResponse = z.object({
content: z.string().nullish(),
metadata: z.record(z.string(), z.unknown()).nullish(),
score: z.number(),
title: z.string().nullish(),
})
/**
* BedrockRetrievalResponse
*/
export const zBedrockRetrievalResponse = z.object({
records: z.array(zBedrockRetrievalRecordResponse),
})
export const zPostTestRetrievalBody = zBedrockRetrievalPayload export const zPostTestRetrievalBody = zBedrockRetrievalPayload
/** /**
* Bedrock retrieval test completed * Bedrock retrieval test completed
*/ */
export const zPostTestRetrievalResponse = zExternalRetrievalTestResponse export const zPostTestRetrievalResponse = zBedrockRetrievalResponse

View File

@ -1474,16 +1474,20 @@ export const get13 = oc
.output(zGetDatasetsByDatasetIdDocumentsByDocumentIdResponse) .output(zGetDatasetsByDatasetIdDocumentsByDocumentIdResponse)
/** /**
* Update an existing document by uploading a file * Update Document by File
*
* Update an existing document by uploading a new file. Re-triggers indexing use the returned `batch` ID with [Get Document Indexing Status](/api-reference/documents/get-document-indexing-status) to track progress.
*/ */
export const patch4 = oc export const patch4 = oc
.route({ .route({
description: 'Update an existing document by uploading a file', description:
'Update an existing document by uploading a new file. Re-triggers indexing — use the returned `batch` ID with [Get Document Indexing Status](/api-reference/documents/get-document-indexing-status) to track progress.',
inputStructure: 'detailed', inputStructure: 'detailed',
method: 'PATCH', method: 'PATCH',
operationId: 'patchDatasetsByDatasetIdDocumentsByDocumentId', operationId: 'patchDatasetsByDatasetIdDocumentsByDocumentId',
path: '/datasets/{dataset_id}/documents/{document_id}', path: '/datasets/{dataset_id}/documents/{document_id}',
tags: ['service_api'], summary: 'Update Document by File',
tags: ['Documents'],
}) })
.input( .input(
z.object({ z.object({

View File

@ -593,40 +593,45 @@ export type DocumentBatchDownloadZipPayload = {
} }
export type DocumentDetailResponse = { export type DocumentDetailResponse = {
archived?: boolean | null archived?: boolean
average_segment_length?: number | null average_segment_length?: number | number
completed_at?: number | null completed_at?: number | null
created_at?: number | null created_at?: number
created_by?: string | null created_by?: string
created_from?: string | null created_from?: string
data_source_info?: { data_source_info?: {
[key: string]: unknown [key: string]: unknown
} | null }
data_source_type?: string | null data_source_type?: string
dataset_process_rule?: { dataset_process_rule?: {
[key: string]: unknown [key: string]: unknown
} | null }
dataset_process_rule_id?: string | null dataset_process_rule_id?: string | null
disabled_at?: number | null disabled_at?: number | null
disabled_by?: string | null disabled_by?: string | null
display_status?: string | null display_status?: string | null
doc_form?: string | null doc_form?: string
doc_language?: string | null doc_language?: string | null
doc_metadata?: Array<DocumentMetadataResponse> | null doc_metadata?:
| Array<DocumentMetadataResponse>
| {
[key: string]: unknown
}
| null
doc_type?: string | null doc_type?: string | null
document_process_rule?: { document_process_rule?: {
[key: string]: unknown [key: string]: unknown
} | null }
enabled?: boolean | null enabled?: boolean
error?: string | null error?: string | null
hit_count?: number | null hit_count?: number
id: string id: string
indexing_latency?: number | null indexing_latency?: number | null
indexing_status?: string | null indexing_status?: string
name?: string | null name?: string
need_summary?: boolean | null need_summary?: boolean
position?: number | null position?: number
segment_count?: number | null segment_count?: number
summary_index_status?: string | null summary_index_status?: string | null
tokens?: number | null tokens?: number | null
updated_at?: number | null updated_at?: number | null
@ -2627,6 +2632,7 @@ export type PatchDatasetsByDatasetIdDocumentsByDocumentIdData = {
} }
export type PatchDatasetsByDatasetIdDocumentsByDocumentIdErrors = { export type PatchDatasetsByDatasetIdDocumentsByDocumentIdErrors = {
400: unknown
401: unknown 401: unknown
403: unknown 403: unknown
404: unknown 404: unknown

View File

@ -761,34 +761,36 @@ export const zDocumentMetadataResponse = z.object({
* DocumentDetailResponse * DocumentDetailResponse
*/ */
export const zDocumentDetailResponse = z.object({ export const zDocumentDetailResponse = z.object({
archived: z.boolean().nullish(), archived: z.boolean().optional(),
average_segment_length: z.number().nullish(), average_segment_length: z.union([z.int(), z.number()]).optional(),
completed_at: z.int().nullish(), completed_at: z.int().nullish(),
created_at: z.int().nullish(), created_at: z.int().optional(),
created_by: z.string().nullish(), created_by: z.string().optional(),
created_from: z.string().nullish(), created_from: z.string().optional(),
data_source_info: z.record(z.string(), z.unknown()).nullish(), data_source_info: z.record(z.string(), z.unknown()).optional(),
data_source_type: z.string().nullish(), data_source_type: z.string().optional(),
dataset_process_rule: z.record(z.string(), z.unknown()).nullish(), dataset_process_rule: z.record(z.string(), z.unknown()).optional(),
dataset_process_rule_id: z.string().nullish(), dataset_process_rule_id: z.string().nullish(),
disabled_at: z.int().nullish(), disabled_at: z.int().nullish(),
disabled_by: z.string().nullish(), disabled_by: z.string().nullish(),
display_status: z.string().nullish(), display_status: z.string().nullish(),
doc_form: z.string().nullish(), doc_form: z.string().optional(),
doc_language: z.string().nullish(), doc_language: z.string().nullish(),
doc_metadata: z.array(zDocumentMetadataResponse).nullish(), doc_metadata: z
.union([z.array(zDocumentMetadataResponse), z.record(z.string(), z.unknown())])
.nullish(),
doc_type: z.string().nullish(), doc_type: z.string().nullish(),
document_process_rule: z.record(z.string(), z.unknown()).nullish(), document_process_rule: z.record(z.string(), z.unknown()).optional(),
enabled: z.boolean().nullish(), enabled: z.boolean().optional(),
error: z.string().nullish(), error: z.string().nullish(),
hit_count: z.int().nullish(), hit_count: z.int().optional(),
id: z.string(), id: z.string(),
indexing_latency: z.number().nullish(), indexing_latency: z.number().nullish(),
indexing_status: z.string().nullish(), indexing_status: z.string().optional(),
name: z.string().nullish(), name: z.string().optional(),
need_summary: z.boolean().nullish(), need_summary: z.boolean().optional(),
position: z.int().nullish(), position: z.int().optional(),
segment_count: z.int().nullish(), segment_count: z.int().optional(),
summary_index_status: z.string().nullish(), summary_index_status: z.string().nullish(),
tokens: z.int().nullish(), tokens: z.int().nullish(),
updated_at: z.int().nullish(), updated_at: z.int().nullish(),
@ -2759,7 +2761,7 @@ export const zPatchDatasetsByDatasetIdDocumentsByDocumentIdPath = z.object({
}) })
/** /**
* Document updated successfully * Document updated successfully.
*/ */
export const zPatchDatasetsByDatasetIdDocumentsByDocumentIdResponse = zDocumentAndBatchResponse export const zPatchDatasetsByDatasetIdDocumentsByDocumentIdResponse = zDocumentAndBatchResponse