From 1cde846bcf2f4925bfdb05c008162e5604ea483d Mon Sep 17 00:00:00 2001 From: Stephen Zhou Date: Mon, 27 Jul 2026 12:52:44 +0800 Subject: [PATCH] fix(dataset): integrate New RAG with KnowledgeFS (#39621) --- api/.env.example | 3 - api/configs/extra/knowledge_fs_config.py | 6 +- api/constants/__init__.py | 2 + .../console/knowledge_fs/resources.py | 387 +- api/controllers/inner_api/plugin/wraps.py | 24 +- .../service_api/knowledge_fs/resources.py | 3 + api/extensions/ext_blueprints.py | 17 +- api/knowledge-fs-contract.lock.json | 6 +- api/knowledge-fs-product-operations.json | 15 + api/services/feature_service.py | 8 + .../knowledge_fs/capability_broker.py | 4 +- api/services/knowledge_fs/data_facade.py | 271 ++ api/services/knowledge_fs/product_dto.py | 181 +- .../knowledge_fs/product_operations.py | 183 + api/services/knowledge_fs/product_remote.py | 5 + .../knowledge_fs/product_remote_http.py | 11 + api/services/knowledge_fs/product_service.py | 8 +- api/services/knowledge_fs_capability.py | 105 + .../configs/test_knowledge_fs_config.py | 2 + .../inner_api/plugin/test_plugin_wraps.py | 47 +- .../test_knowledge_fs_product_controllers.py | 26 + .../test_knowledge_fs_resource_delegation.py | 27 +- .../extensions/test_ext_blueprints_cors.py | 36 + .../test_feature_service_knowledge_fs.py | 36 + .../services/test_knowledge_fs_data_facade.py | 105 + .../test_knowledge_fs_product_operations.py | 15 + .../test_knowledge_fs_product_remote_http.py | 37 +- docker/envs/core-services/api.env.example | 3 +- .../core-services/knowledge-fs.env.example | 5 + .../apps/api/src/auth-options.test.ts | 58 + knowledge-fs/apps/api/src/auth-options.ts | 27 +- .../dify-datasource-invocation-client.test.ts | 42 + .../src/dify-datasource-invocation-client.ts | 24 +- .../api/src/website-crawl-options.test.ts | 6 +- .../apps/api/src/website-crawl-options.ts | 17 +- ...se-deletion-lifecycle-fence-reader.test.ts | 4 + ...atabase-deletion-lifecycle-fence-reader.ts | 6 +- ...rable-deletion-target-capabilities.test.ts | 148 +- ...se-durable-deletion-target-capabilities.ts | 64 +- .../api/src/database-row-utils.test.ts | 9 + .../packages/api/src/database-row-utils.ts | 11 + .../api/src/dify-capability-v2.test.ts | 6 + .../packages/api/src/dify-capability-v2.ts | 145 + ...ent-compilation-attempt-repository.test.ts | 4 +- ...document-compilation-attempt-repository.ts | 34 +- ...ompilation-publication-coordinator.test.ts | 84 + ...ent-compilation-publication-coordinator.ts | 84 +- .../api/src/document-write-handlers.ts | 89 +- .../src/durable-deletion-repository.test.ts | 19 +- .../api/src/durable-deletion-repository.ts | 7 +- .../api/src/gateway-document-write.test.ts | 83 + knowledge-fs/packages/api/src/gateway.test.ts | 21 - .../packages/api/src/index-reindexer.test.ts | 15 +- .../packages/api/src/index-reindexer.ts | 11 +- .../api/src/logical-document-repository.ts | 3 +- .../src/page-index-build-repository.test.ts | 6 +- .../api/src/page-index-build-repository.ts | 13 +- .../packages/api/src/source-product-routes.ts | 13 + ...oduct-workflow-database-repository.test.ts | 148 +- ...ce-product-workflow-database-repository.ts | 185 +- .../source-product-workflow-runtime.test.ts | 25 +- .../src/source-product-workflow-runtime.ts | 27 + .../src/dify-model-runtime-llm.test.ts | 24 + knowledge-fs/packages/generation/src/index.ts | 2 +- .../export-capability-v2-operations.test.mjs | 41 +- knowledge-fs/scripts/export-openapi.mjs | 16 +- knowledge-fs/scripts/export-openapi.test.mjs | 17 + packages/contracts/console.ts | 8 +- .../api/console/knowledge-fs/orpc.gen.ts | 698 +++- .../api/console/knowledge-fs/types.gen.ts | 680 +++- .../api/console/knowledge-fs/zod.gen.ts | 667 +++- .../api/console/system-features/types.gen.ts | 1 + .../api/console/system-features/zod.gen.ts | 1 + .../contracts/generated/api/web/types.gen.ts | 1 + .../contracts/generated/api/web/zod.gen.ts | 1 + .../generated/knowledge-fs/metadata.gen.ts | 12 - .../generated/knowledge-fs/orpc.gen.ts | 1568 -------- .../generated/knowledge-fs/types.gen.ts | 3302 ----------------- .../generated/knowledge-fs/zod.gen.ts | 2257 ----------- .../contracts/knowledge-fs-contract.test.mjs | 47 - .../openapi-ts.knowledge-fs.config.ts | 61 - packages/contracts/package.json | 5 - .../generate-knowledge-fs-contract.mjs | 119 - .../scripts/knowledge-fs-contract-utils.mjs | 19 - web/context/system-features-state.ts | 4 + .../__tests__/add-source-page.spec.tsx | 306 +- .../__tests__/crawl-selection-form.spec.tsx | 116 +- .../__tests__/create-knowledge-page.spec.tsx | 279 +- .../create-knowledge-workflow.spec.ts | 131 + .../__tests__/document-detail-model.spec.ts | 2 +- .../__tests__/document-detail-page.spec.tsx | 482 ++- .../new-rag/__tests__/document-model.spec.ts | 5 +- .../new-rag/__tests__/documents-page.spec.tsx | 416 ++- .../__tests__/knowledge-fs-upload.spec.ts | 154 + .../__tests__/knowledge-space-shell.spec.tsx | 26 +- .../knowledge-view-switcher.spec.tsx | 38 + .../__tests__/new-knowledge-list.spec.tsx | 77 +- .../__tests__/processing-task-events.spec.ts | 191 +- .../new-rag/__tests__/sources-page.spec.tsx | 119 +- .../__tests__/website-crawl-preview.spec.tsx | 278 +- web/features/new-rag/add-source-page.tsx | 171 +- .../components/knowledge-space-card.tsx | 26 +- web/features/new-rag/crawl-selection-form.tsx | 99 +- .../new-rag/create-knowledge-page.tsx | 87 +- .../new-rag/create-knowledge-workflow.ts | 124 +- web/features/new-rag/create-source-setup.tsx | 14 +- web/features/new-rag/create-upload-queue.tsx | 2 +- .../new-rag/document-chunk-detail.tsx | 2 +- .../new-rag/document-detail-header.tsx | 5 +- web/features/new-rag/document-detail-model.ts | 2 +- web/features/new-rag/document-detail-page.tsx | 37 +- .../new-rag/document-detail-queries.ts | 16 +- .../new-rag/document-detail-status.tsx | 2 +- web/features/new-rag/document-list.tsx | 10 +- web/features/new-rag/document-model.ts | 5 +- web/features/new-rag/document-models.ts | 256 ++ .../new-rag/document-revision-content.tsx | 12 +- web/features/new-rag/documents-page.tsx | 188 +- web/features/new-rag/knowledge-fs-upload.ts | 325 ++ .../new-rag/knowledge-space-shell.tsx | 12 +- web/features/new-rag/new-knowledge-list.tsx | 23 +- .../new-rag/processing-tasks-drawer.tsx | 59 +- .../services/processing-task-events.ts | 273 +- web/features/new-rag/source-models.ts | 236 ++ web/features/new-rag/sources-page.tsx | 69 +- web/features/new-rag/use-document-reindex.ts | 8 +- .../new-rag/use-document-task-status.ts | 239 +- .../new-rag/website-crawl-preview.tsx | 207 +- web/i18n/ar-TN/dataset.json | 2 + web/i18n/de-DE/dataset.json | 2 + web/i18n/en-US/dataset.json | 4 +- web/i18n/es-ES/dataset.json | 2 + web/i18n/fa-IR/dataset.json | 2 + web/i18n/fr-FR/dataset.json | 2 + web/i18n/hi-IN/dataset.json | 2 + web/i18n/id-ID/dataset.json | 2 + web/i18n/it-IT/dataset.json | 2 + web/i18n/ja-JP/dataset.json | 2 + web/i18n/ko-KR/dataset.json | 2 + web/i18n/nl-NL/dataset.json | 2 + web/i18n/pl-PL/dataset.json | 2 + web/i18n/pt-BR/dataset.json | 2 + web/i18n/ro-RO/dataset.json | 2 + web/i18n/ru-RU/dataset.json | 2 + web/i18n/sl-SI/dataset.json | 2 + web/i18n/th-TH/dataset.json | 2 + web/i18n/tr-TR/dataset.json | 2 + web/i18n/uk-UA/dataset.json | 2 + web/i18n/vi-VN/dataset.json | 2 + web/i18n/zh-Hans/dataset.json | 2 + web/i18n/zh-Hant/dataset.json | 2 + web/service/client.spec.ts | 69 +- web/service/console-router-loader.ts | 6 - web/test/console/system-features.ts | 1 + 154 files changed, 8666 insertions(+), 9154 deletions(-) create mode 100644 api/tests/unit_tests/extensions/test_ext_blueprints_cors.py create mode 100644 api/tests/unit_tests/services/test_feature_service_knowledge_fs.py delete mode 100644 packages/contracts/generated/knowledge-fs/metadata.gen.ts delete mode 100644 packages/contracts/generated/knowledge-fs/orpc.gen.ts delete mode 100644 packages/contracts/generated/knowledge-fs/types.gen.ts delete mode 100644 packages/contracts/generated/knowledge-fs/zod.gen.ts delete mode 100644 packages/contracts/knowledge-fs-contract.test.mjs delete mode 100644 packages/contracts/openapi-ts.knowledge-fs.config.ts delete mode 100644 packages/contracts/scripts/generate-knowledge-fs-contract.mjs delete mode 100644 packages/contracts/scripts/knowledge-fs-contract-utils.mjs create mode 100644 web/features/new-rag/__tests__/create-knowledge-workflow.spec.ts create mode 100644 web/features/new-rag/__tests__/knowledge-fs-upload.spec.ts create mode 100644 web/features/new-rag/__tests__/knowledge-view-switcher.spec.tsx create mode 100644 web/features/new-rag/document-models.ts create mode 100644 web/features/new-rag/knowledge-fs-upload.ts create mode 100644 web/features/new-rag/source-models.ts diff --git a/api/.env.example b/api/.env.example index 26a7f5dabfb..fd11002f17d 100644 --- a/api/.env.example +++ b/api/.env.example @@ -692,8 +692,6 @@ KNOWLEDGE_FS_LEGACY_ACL_FREEZE_READY=false KNOWLEDGE_FS_LIFECYCLE_POLL_INTERVAL_SECONDS=15 KNOWLEDGE_FS_LIFECYCLE_LEASE_SECONDS=60 KNOWLEDGE_FS_LIFECYCLE_BATCH_SIZE=25 -# Legacy rollback-only HMAC; Capability v2 deployments leave this blank. -KNOWLEDGE_FS_JWT_SECRET= KNOWLEDGE_FS_CAPABILITY_V2_ENABLED=false KNOWLEDGE_FS_CAPABILITY_V2_SIGNING_KID= KNOWLEDGE_FS_CAPABILITY_V2_PRIVATE_KEY_PEM= @@ -701,7 +699,6 @@ KNOWLEDGE_FS_CAPABILITY_V2_PREVIOUS_PUBLIC_JWKS= KNOWLEDGE_FS_CAPABILITY_V2_ISSUER=dify-control-plane KNOWLEDGE_FS_CAPABILITY_V2_AUDIENCE=knowledge-fs KNOWLEDGE_FS_CAPABILITY_V2_MAX_TTL_SECONDS=60 -KNOWLEDGE_FS_SSE_READ_TIMEOUT_SECONDS=300 KNOWLEDGE_FS_TIMEOUT_SECONDS=10 KNOWLEDGE_FS_JWKS_CACHE_MAX_AGE_SECONDS=300 KNOWLEDGE_FS_PRODUCT_MAX_RESPONSE_BYTES=4194304 diff --git a/api/configs/extra/knowledge_fs_config.py b/api/configs/extra/knowledge_fs_config.py index fa511248c7f..e74ae479198 100644 --- a/api/configs/extra/knowledge_fs_config.py +++ b/api/configs/extra/knowledge_fs_config.py @@ -12,7 +12,7 @@ class KnowledgeFSConfig(BaseSettings): KNOWLEDGE_FS_ENABLED: bool = Field( default=False, - description="Enable the private KnowledgeFS Console bridge.", + description="Enable the KnowledgeFS control-plane product routes.", ) KNOWLEDGE_FS_LIFECYCLE_WORKER_ENABLED: bool = Field( default=False, @@ -34,6 +34,10 @@ class KnowledgeFSConfig(BaseSettings): default=None, description="Public KnowledgeFS origin returned with direct upload capabilities.", ) + KNOWLEDGE_FS_DIRECT_UPLOAD_READY: bool = Field( + default=False, + description="Confirm that KnowledgeFS direct upload and its browser origin policy are deployed and verified.", + ) KNOWLEDGE_FS_CAPABILITY_V2_ENABLED: bool = Field( default=False, description="Prepare resource-scoped Capability v2 issuance; disabled until rollout approval.", diff --git a/api/constants/__init__.py b/api/constants/__init__.py index 8698fb855de..17220d7d15f 100644 --- a/api/constants/__init__.py +++ b/api/constants/__init__.py @@ -77,3 +77,5 @@ COOKIE_NAME_PASSPORT = "passport" HEADER_NAME_CSRF_TOKEN = "X-CSRF-Token" HEADER_NAME_APP_CODE = "X-App-Code" HEADER_NAME_PASSPORT = "X-App-Passport" +HEADER_NAME_IDEMPOTENCY_KEY = "Idempotency-Key" +HEADER_NAME_REQUEST_ID = "X-Request-ID" diff --git a/api/controllers/console/knowledge_fs/resources.py b/api/controllers/console/knowledge_fs/resources.py index deb9ce71249..6573308c79b 100644 --- a/api/controllers/console/knowledge_fs/resources.py +++ b/api/controllers/console/knowledge_fs/resources.py @@ -64,6 +64,9 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSBulkDocumentDeletePayload, KnowledgeFSBulkJobResponse, KnowledgeFSCapabilityResponse, + KnowledgeFSCrawlPreviewPageListQuery, + KnowledgeFSCrawlPreviewPageListResponse, + KnowledgeFSCrawlPreviewSelectionPayload, KnowledgeFSCredentialCreatePayload, KnowledgeFSCredentialCreateResponse, KnowledgeFSCredentialListResponse, @@ -86,6 +89,7 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSExternalAccessResponse, KnowledgeFSIdempotencyHeader, KnowledgeFSJWKSResponse, + KnowledgeFSLogicalDocumentListResponse, KnowledgeFSLogicalDocumentResponse, KnowledgeFSMembersReplacePayload, KnowledgeFSOverviewBaseStatsResponse, @@ -111,6 +115,11 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSSettingsPayload, KnowledgeFSSettingsResponse, KnowledgeFSSmallFileUploadResponse, + KnowledgeFSSourceConnectionCreatePayload, + KnowledgeFSSourceConnectionListQuery, + KnowledgeFSSourceConnectionListResponse, + KnowledgeFSSourceConnectionRefreshPayload, + KnowledgeFSSourceConnectionResponse, KnowledgeFSSourceCrawlResponse, KnowledgeFSSourceCreatePayload, KnowledgeFSSourceCredentialTestResponse, @@ -124,8 +133,13 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSSourceListResponse, KnowledgeFSSourcePagesQuery, KnowledgeFSSourcePagesResponse, + KnowledgeFSSourceProviderListResponse, KnowledgeFSSourceResponse, + KnowledgeFSSourceSyncPolicyPayload, + KnowledgeFSSourceSyncPolicyResponse, KnowledgeFSSourceUpdatePayload, + KnowledgeFSSourceWorkflowCancelPayload, + KnowledgeFSSourceWorkflowResponse, KnowledgeFSSpaceCreatePayload, KnowledgeFSSpaceCreateResponse, KnowledgeFSSpaceDetailResponse, @@ -143,6 +157,7 @@ from services.knowledge_fs.product_remote import ( KnowledgeFSOperationUnavailableError, KnowledgeFSProductRemoteError, KnowledgeFSProductRequestRejectedError, + KnowledgeFSProductResourceNotFoundError, ) from services.knowledge_fs.runtime import KnowledgeFSRuntime, create_knowledge_fs_runtime from services.knowledge_fs_capability import ( @@ -164,6 +179,7 @@ register_schema_models( KnowledgeFSDocumentMetadataPayload, KnowledgeFSDocumentReindexPayload, KnowledgeFSExternalAccessPayload, + KnowledgeFSCrawlPreviewPageListQuery, KnowledgeFSMembersReplacePayload, KnowledgeFSQueryCreatePayload, KnowledgeFSResearchTaskPartialsQuery, @@ -171,6 +187,10 @@ register_schema_models( KnowledgeFSResearchTaskCreatePayload, KnowledgeFSSettingsPayload, KnowledgeFSSourceCreatePayload, + KnowledgeFSSourceConnectionCreatePayload, + KnowledgeFSSourceConnectionListQuery, + KnowledgeFSSourceConnectionRefreshPayload, + KnowledgeFSCrawlPreviewSelectionPayload, KnowledgeFSSourceDeletePayload, KnowledgeFSSourceDeleteQuery, KnowledgeFSSourceFilesQuery, @@ -178,6 +198,8 @@ register_schema_models( KnowledgeFSSourceImportPagesPayload, KnowledgeFSSourcePagesQuery, KnowledgeFSSourceUpdatePayload, + KnowledgeFSSourceSyncPolicyPayload, + KnowledgeFSSourceWorkflowCancelPayload, KnowledgeFSSpaceCreatePayload, KnowledgeFSSpaceListQuery, KnowledgeFSSpaceUpdatePayload, @@ -208,6 +230,7 @@ register_response_schema_models( KnowledgeFSDurableDeletionAcceptedResponse, KnowledgeFSExternalAccessResponse, KnowledgeFSJWKSResponse, + KnowledgeFSLogicalDocumentListResponse, KnowledgeFSPermissionListResponse, KnowledgeFSQueryResponse, KnowledgeFSQueryAdmissionResponse, @@ -218,6 +241,9 @@ register_response_schema_models( KnowledgeFSResearchTaskListResponse, KnowledgeFSSettingsResponse, KnowledgeFSSmallFileUploadResponse, + KnowledgeFSCrawlPreviewPageListResponse, + KnowledgeFSSourceConnectionListResponse, + KnowledgeFSSourceConnectionResponse, KnowledgeFSSourceListResponse, KnowledgeFSSourceCrawlResponse, KnowledgeFSSourceCredentialTestResponse, @@ -225,6 +251,9 @@ register_response_schema_models( KnowledgeFSSourceImportResponse, KnowledgeFSSourcePagesResponse, KnowledgeFSSourceResponse, + KnowledgeFSSourceProviderListResponse, + KnowledgeFSSourceSyncPolicyResponse, + KnowledgeFSSourceWorkflowResponse, KnowledgeFSSpaceCreateResponse, KnowledgeFSSpaceDetailResponse, KnowledgeFSSpaceListResponse, @@ -255,6 +284,8 @@ def _knowledge_fs_errors[**P, R](view: Callable[P, R]) -> Callable[P, R]: raise KnowledgeFSSpaceNotFoundHTTPError() from exc except KnowledgeFSOperationUnavailableError as exc: raise KnowledgeFSOperationUnavailableHTTPError() from exc + except KnowledgeFSProductResourceNotFoundError as exc: + raise NotFound() from exc except KnowledgeFSProductRemoteError as exc: raise KnowledgeFSUpstreamUnavailableHTTPError() from exc except KnowledgeFSOperationRateLimitExceededError as exc: @@ -290,6 +321,14 @@ _SMALL_FILE_UPLOAD_PARAMS = { "required": True, } } +_IDEMPOTENCY_HEADER_PARAMS = { + "Idempotency-Key": { + "description": "Stable key used to make the mutation safe to retry", + "in": "header", + "required": True, + "type": "string", + } +} _SMALL_FILE_MULTIPART_OVERHEAD_MAX_BYTES = 64 * 1024 _BACKGROUND_TASK_KIND_ADAPTER: TypeAdapter[Literal["document", "document_bulk", "source"]] = TypeAdapter( Literal["document", "document_bulk", "source"] @@ -822,6 +861,52 @@ class KnowledgeFSSpaceOverviewHealthApi(Resource): return dump_response(KnowledgeFSOverviewHealthResponse, result) +@console_ns.route("/knowledge-fs/spaces//logical-documents") +class KnowledgeFSSpaceLogicalDocumentsApi(Resource): + @console_ns.doc(params=query_params_from_model(KnowledgeFSCursorQuery)) + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS logical documents", + console_ns.models[KnowledgeFSLogicalDocumentListResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def get(self, control_space_id: str): + actor_id, tenant_id = _actor() + query = KnowledgeFSCursorQuery.model_validate(request.args.to_dict()) + result = _console_services().facade.list_logical_documents( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + cursor=query.cursor, + ) + return dump_response(KnowledgeFSLogicalDocumentListResponse, result) + + +@console_ns.route("/knowledge-fs/spaces//logical-documents/") +class KnowledgeFSSpaceLogicalDocumentApi(Resource): + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS logical document", + console_ns.models[KnowledgeFSLogicalDocumentResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def get(self, control_space_id: str, document_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.get_logical_document( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + document_id=document_id, + ) + return dump_response(KnowledgeFSLogicalDocumentResponse, result) + + @console_ns.route("/knowledge-fs/spaces//documents") class KnowledgeFSSpaceDocumentsApi(Resource): @console_ns.doc(params=query_params_from_model(KnowledgeFSCursorQuery)) @@ -866,6 +951,7 @@ class KnowledgeFSSpaceDocumentsApi(Resource): @console_ns.route("/knowledge-fs/spaces//documents/bulk") class KnowledgeFSSpaceBulkDocumentsApi(Resource): @console_ns.expect(console_ns.models[KnowledgeFSBulkDocumentDeletePayload.__name__]) + @console_ns.doc(params=_IDEMPOTENCY_HEADER_PARAMS) @console_ns.response( HTTPStatus.ACCEPTED, "KnowledgeFS document deletions accepted", @@ -953,6 +1039,7 @@ class KnowledgeFSSpaceDocumentApi(Resource): return dump_response(KnowledgeFSLogicalDocumentResponse, result) @console_ns.expect(console_ns.models[KnowledgeFSDocumentDeletePayload.__name__]) + @console_ns.doc(params=_IDEMPOTENCY_HEADER_PARAMS) @console_ns.response( HTTPStatus.ACCEPTED, "KnowledgeFS document deletion accepted", @@ -1225,6 +1312,96 @@ class KnowledgeFSSpaceBackgroundTaskRetryApi(Resource): return dump_response(KnowledgeFSBackgroundTaskResponse, result) +@console_ns.route("/knowledge-fs/spaces//source-providers") +class KnowledgeFSSourceProvidersApi(Resource): + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS source providers", + console_ns.models[KnowledgeFSSourceProviderListResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def get(self, control_space_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.list_source_providers( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + ) + return dump_response(KnowledgeFSSourceProviderListResponse, result) + + +@console_ns.route("/knowledge-fs/spaces//source-connections") +class KnowledgeFSSourceConnectionsApi(Resource): + @console_ns.doc(params=query_params_from_model(KnowledgeFSSourceConnectionListQuery)) + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS source connections", + console_ns.models[KnowledgeFSSourceConnectionListResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def get(self, control_space_id: str): + actor_id, tenant_id = _actor() + query = KnowledgeFSSourceConnectionListQuery.model_validate(request.args.to_dict()) + result = _console_services().facade.list_source_connections( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + cursor=query.cursor, + limit=query.limit, + ) + return dump_response(KnowledgeFSSourceConnectionListResponse, result) + + @console_ns.expect(console_ns.models[KnowledgeFSSourceConnectionCreatePayload.__name__]) + @console_ns.response( + HTTPStatus.CREATED, + "KnowledgeFS source connection created", + console_ns.models[KnowledgeFSSourceConnectionResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def post(self, control_space_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.create_source_connection( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + payload=_payload(KnowledgeFSSourceConnectionCreatePayload), + ) + return dump_response(KnowledgeFSSourceConnectionResponse, result), HTTPStatus.CREATED + + +@console_ns.route("/knowledge-fs/spaces//source-connections//refresh") +class KnowledgeFSSourceConnectionRefreshApi(Resource): + @console_ns.expect(console_ns.models[KnowledgeFSSourceConnectionRefreshPayload.__name__]) + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS source connection refreshed", + console_ns.models[KnowledgeFSSourceConnectionResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def post(self, control_space_id: str, connection_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.refresh_source_connection( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + connection_id=connection_id, + payload=_payload(KnowledgeFSSourceConnectionRefreshPayload), + ) + return dump_response(KnowledgeFSSourceConnectionResponse, result) + + @console_ns.route("/knowledge-fs/spaces//sources") class KnowledgeFSSpaceSourcesApi(Resource): @console_ns.doc(params=query_params_from_model(KnowledgeFSCursorQuery)) @@ -1303,7 +1480,7 @@ class KnowledgeFSSpaceSourceApi(Resource): return dump_response(KnowledgeFSSourceResponse, result) @console_ns.expect(console_ns.models[KnowledgeFSSourceDeletePayload.__name__]) - @console_ns.doc(params=query_params_from_model(KnowledgeFSSourceDeleteQuery)) + @console_ns.doc(params=query_params_from_model(KnowledgeFSSourceDeleteQuery) | _IDEMPOTENCY_HEADER_PARAMS) @console_ns.response( HTTPStatus.ACCEPTED, "KnowledgeFS source deletion accepted", @@ -1347,10 +1524,13 @@ class KnowledgeFSSpaceSourceTestApi(Resource): return dump_response(KnowledgeFSSourceCredentialTestResponse, result) -@console_ns.route("/knowledge-fs/spaces//sources//crawl") -class KnowledgeFSSpaceSourceCrawlApi(Resource): +@console_ns.route("/knowledge-fs/spaces//sources//sync") +class KnowledgeFSSpaceSourceSyncApi(Resource): + @console_ns.doc(params=_IDEMPOTENCY_HEADER_PARAMS) @console_ns.response( - HTTPStatus.OK, "KnowledgeFS source crawl", console_ns.models[KnowledgeFSSourceCrawlResponse.__name__] + HTTPStatus.ACCEPTED, + "KnowledgeFS source sync accepted", + console_ns.models[KnowledgeFSSourceWorkflowResponse.__name__], ) @setup_required @login_required @@ -1358,10 +1538,201 @@ class KnowledgeFSSpaceSourceCrawlApi(Resource): @_knowledge_fs_errors def post(self, control_space_id: str, source_id: str): actor_id, tenant_id = _actor() - result = _console_services().facade.crawl_source( - tenant_id=tenant_id, account_id=actor_id, control_space_id=control_space_id, source_id=source_id + result = _console_services().facade.sync_source( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + source_id=source_id, + idempotency_key=_idempotency_key(), ) - return dump_response(KnowledgeFSSourceCrawlResponse, result) + return dump_response(KnowledgeFSSourceWorkflowResponse, result), HTTPStatus.ACCEPTED + + +@console_ns.route("/knowledge-fs/spaces//sources//crawl-preview") +class KnowledgeFSSpaceSourceCrawlPreviewApi(Resource): + @console_ns.doc(params=_IDEMPOTENCY_HEADER_PARAMS) + @console_ns.response( + HTTPStatus.ACCEPTED, + "KnowledgeFS source crawl preview accepted", + console_ns.models[KnowledgeFSSourceWorkflowResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def post(self, control_space_id: str, source_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.preview_source_crawl( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + source_id=source_id, + idempotency_key=_idempotency_key(), + ) + return dump_response(KnowledgeFSSourceWorkflowResponse, result), HTTPStatus.ACCEPTED + + +@console_ns.route("/knowledge-fs/spaces//sources//sync-policy") +class KnowledgeFSSpaceSourceSyncPolicyApi(Resource): + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS source sync policy", + console_ns.models[KnowledgeFSSourceSyncPolicyResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def get(self, control_space_id: str, source_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.get_source_sync_policy( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + source_id=source_id, + ) + return dump_response(KnowledgeFSSourceSyncPolicyResponse, result) + + @console_ns.expect(console_ns.models[KnowledgeFSSourceSyncPolicyPayload.__name__]) + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS source sync policy updated", + console_ns.models[KnowledgeFSSourceSyncPolicyResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def put(self, control_space_id: str, source_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.update_source_sync_policy( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + source_id=source_id, + payload=_payload(KnowledgeFSSourceSyncPolicyPayload), + ) + return dump_response(KnowledgeFSSourceSyncPolicyResponse, result) + + +@console_ns.route("/knowledge-fs/spaces//source-workflows/") +class KnowledgeFSSourceWorkflowApi(Resource): + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS source workflow", + console_ns.models[KnowledgeFSSourceWorkflowResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def get(self, control_space_id: str, run_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.get_source_workflow( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + run_id=run_id, + ) + return dump_response(KnowledgeFSSourceWorkflowResponse, result) + + +@console_ns.route("/knowledge-fs/spaces//source-workflows//cancel") +class KnowledgeFSSourceWorkflowCancelApi(Resource): + @console_ns.expect(console_ns.models[KnowledgeFSSourceWorkflowCancelPayload.__name__]) + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS source workflow canceled", + console_ns.models[KnowledgeFSSourceWorkflowResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def post(self, control_space_id: str, run_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.cancel_source_workflow( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + run_id=run_id, + payload=_payload(KnowledgeFSSourceWorkflowCancelPayload), + ) + return dump_response(KnowledgeFSSourceWorkflowResponse, result) + + +@console_ns.route("/knowledge-fs/spaces//source-workflows//retry") +class KnowledgeFSSourceWorkflowRetryApi(Resource): + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS source workflow retried", + console_ns.models[KnowledgeFSSourceWorkflowResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def post(self, control_space_id: str, run_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.retry_source_workflow( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + run_id=run_id, + ) + return dump_response(KnowledgeFSSourceWorkflowResponse, result) + + +@console_ns.route("/knowledge-fs/spaces//source-workflows//pages") +class KnowledgeFSSourceWorkflowPagesApi(Resource): + @console_ns.doc(params=query_params_from_model(KnowledgeFSCrawlPreviewPageListQuery)) + @console_ns.response( + HTTPStatus.OK, + "KnowledgeFS crawl preview pages", + console_ns.models[KnowledgeFSCrawlPreviewPageListResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def get(self, control_space_id: str, run_id: str): + actor_id, tenant_id = _actor() + query = KnowledgeFSCrawlPreviewPageListQuery.model_validate(request.args.to_dict()) + result = _console_services().facade.list_crawl_preview_pages( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + run_id=run_id, + cursor=query.cursor, + limit=query.limit, + ) + return dump_response(KnowledgeFSCrawlPreviewPageListResponse, result) + + +@console_ns.route("/knowledge-fs/spaces//source-workflows//selection") +class KnowledgeFSSourceWorkflowSelectionApi(Resource): + @console_ns.expect(console_ns.models[KnowledgeFSCrawlPreviewSelectionPayload.__name__]) + @console_ns.doc(params=_IDEMPOTENCY_HEADER_PARAMS) + @console_ns.response( + HTTPStatus.ACCEPTED, + "KnowledgeFS crawl preview selection accepted", + console_ns.models[KnowledgeFSSourceWorkflowResponse.__name__], + ) + @setup_required + @login_required + @account_initialization_required + @_knowledge_fs_errors + def post(self, control_space_id: str, run_id: str): + actor_id, tenant_id = _actor() + result = _console_services().facade.select_crawl_preview_pages( + tenant_id=tenant_id, + account_id=actor_id, + control_space_id=control_space_id, + run_id=run_id, + payload=_payload(KnowledgeFSCrawlPreviewSelectionPayload), + idempotency_key=_idempotency_key(), + ) + return dump_response(KnowledgeFSSourceWorkflowResponse, result), HTTPStatus.ACCEPTED @console_ns.route("/knowledge-fs/spaces//sources//pages") @@ -1760,7 +2131,7 @@ class KnowledgeFSSpaceUploadCapabilitiesApi(Resource): @_knowledge_fs_errors def post(self, control_space_id: str): direct_origin = dify_config.KNOWLEDGE_FS_DIRECT_ORIGIN - if direct_origin is None: + if direct_origin is None or not dify_config.KNOWLEDGE_FS_DIRECT_UPLOAD_READY: raise KnowledgeFSOperationUnavailableError("KnowledgeFS direct upload is not configured") actor_id, tenant_id = _actor() payload = _payload(KnowledgeFSUploadCapabilityPayload) diff --git a/api/controllers/inner_api/plugin/wraps.py b/api/controllers/inner_api/plugin/wraps.py index cc68fab6c99..a4dbd74d44e 100644 --- a/api/controllers/inner_api/plugin/wraps.py +++ b/api/controllers/inner_api/plugin/wraps.py @@ -1,5 +1,6 @@ from collections.abc import Callable from functools import wraps +from uuid import UUID from flask import current_app, request from flask_login import user_logged_in @@ -55,14 +56,15 @@ def get_user(tenant_id: str, user_id: str | None) -> EndUser: # session_id, id is auto-generated) and a fresh EndUser # was created per call, breaking multi-turn chat # continuation (see #36736). - user_model = session.scalar( - select(EndUser) - .where( - EndUser.id == user_id, - EndUser.tenant_id == tenant_id, + if _is_uuid(user_id): + user_model = session.scalar( + select(EndUser) + .where( + EndUser.id == user_id, + EndUser.tenant_id == tenant_id, + ) + .limit(1) ) - .limit(1) - ) if user_model is None: user_model = session.scalar( select(EndUser) @@ -90,6 +92,14 @@ def get_user(tenant_id: str, user_id: str | None) -> EndUser: return user_model +def _is_uuid(value: str) -> bool: + try: + UUID(value) + except ValueError: + return False + return True + + def get_user_tenant[**P, R](view_func: Callable[P, R]) -> Callable[P, R]: @wraps(view_func) def decorated_view(*args: P.args, **kwargs: P.kwargs) -> R: diff --git a/api/controllers/service_api/knowledge_fs/resources.py b/api/controllers/service_api/knowledge_fs/resources.py index ef9a5fcef78..947db10d4b9 100644 --- a/api/controllers/service_api/knowledge_fs/resources.py +++ b/api/controllers/service_api/knowledge_fs/resources.py @@ -95,6 +95,7 @@ from services.knowledge_fs.product_operations import product_operation_action from services.knowledge_fs.product_remote import ( KnowledgeFSOperationUnavailableError, KnowledgeFSProductRemoteError, + KnowledgeFSProductResourceNotFoundError, ) from services.knowledge_fs.runtime import KnowledgeFSRuntime, create_knowledge_fs_runtime @@ -171,6 +172,8 @@ def _service_api_errors[**P, R](view: Callable[P, R]) -> Callable[P, R]: raise KnowledgeFSInvalidCredentialHTTPError() from exc except KnowledgeFSOperationUnavailableError as exc: raise KnowledgeFSServiceOperationUnavailableHTTPError() from exc + except KnowledgeFSProductResourceNotFoundError as exc: + raise NotFound() from exc except KnowledgeFSProductRemoteError as exc: raise KnowledgeFSServiceUpstreamUnavailableHTTPError() from exc except KnowledgeFSOperationRateLimitExceededError as exc: diff --git a/api/extensions/ext_blueprints.py b/api/extensions/ext_blueprints.py index 7b1481b3535..51aa2a3db5f 100644 --- a/api/extensions/ext_blueprints.py +++ b/api/extensions/ext_blueprints.py @@ -1,10 +1,23 @@ +"""Register API blueprints with their browser-facing CORS policies.""" + from configs import dify_config -from constants import HEADER_NAME_APP_CODE, HEADER_NAME_CSRF_TOKEN, HEADER_NAME_PASSPORT +from constants import ( + HEADER_NAME_APP_CODE, + HEADER_NAME_CSRF_TOKEN, + HEADER_NAME_IDEMPOTENCY_KEY, + HEADER_NAME_PASSPORT, + HEADER_NAME_REQUEST_ID, +) from dify_app import DifyApp BASE_CORS_HEADERS: tuple[str, ...] = ("Content-Type", HEADER_NAME_APP_CODE, HEADER_NAME_PASSPORT) SERVICE_API_HEADERS: tuple[str, ...] = (*BASE_CORS_HEADERS, "Authorization") -AUTHENTICATED_HEADERS: tuple[str, ...] = (*SERVICE_API_HEADERS, HEADER_NAME_CSRF_TOKEN) +AUTHENTICATED_HEADERS: tuple[str, ...] = ( + *SERVICE_API_HEADERS, + HEADER_NAME_CSRF_TOKEN, + HEADER_NAME_IDEMPOTENCY_KEY, + HEADER_NAME_REQUEST_ID, +) FILES_HEADERS: tuple[str, ...] = (*BASE_CORS_HEADERS, HEADER_NAME_CSRF_TOKEN) EMBED_HEADERS: tuple[str, ...] = ("Content-Type", HEADER_NAME_APP_CODE) EXPOSED_HEADERS: tuple[str, ...] = ("X-Version", "X-Env", "X-Trace-Id") diff --git a/api/knowledge-fs-contract.lock.json b/api/knowledge-fs-contract.lock.json index 01f703c4ed7..0b0eaa07cd5 100644 --- a/api/knowledge-fs-contract.lock.json +++ b/api/knowledge-fs-contract.lock.json @@ -1,9 +1,9 @@ { "schemaVersion": 5, - "subtreeTree": "4a5f77139bfa81192ca1151aa2cea8aca8a19501", - "openapiSha256": "0a03c2cdc027c8d8d792da97db65f642c2e28ecfd8c4a9bc08aafb53fc38a64b", + "subtreeTree": "2745077bf08ffb7143abe8bd2e14fa235136d276", + "openapiSha256": "5e6d37b22f3e0441492bd928429899d890dfcda8d4645c671fa4d128faa8947d", "capabilityV2AuthManifestSha256": "fc0a47e23cce12544882f0298522b4933002e892b84ce1815df7e81d36a7a0c7", "capabilityV2AuthTestVectorSha256": "ae0de37b1ff05c40f905cf17a7b410d8971acacf64db07d5ee3d6fecfa559ce3", - "productOperationManifestSha256": "8dc392325682a307702bc53420616123ab875e6875d3a473a453dca1ac40c61e", + "productOperationManifestSha256": "7b46eaf900d3db5b518d8ab52e3265b7c7bd0b5fbdb831e5647422c9f8975427", "productOperationGapManifestSha256": "ccbae37fe658177a77529822211a2cf02e72b5815cbc9b7e7e65e6817ba5e0a9" } diff --git a/api/knowledge-fs-product-operations.json b/api/knowledge-fs-product-operations.json index fa9588df072..f873c1ea783 100644 --- a/api/knowledge-fs-product-operations.json +++ b/api/knowledge-fs-product-operations.json @@ -11,6 +11,8 @@ {"productOperationId":"getOverviewHealth","kfsOperationId":"getKnowledgeSpaceProductHealth","method":"GET","path":"/knowledge-spaces/{id}/overview/health","action":"knowledge_spaces.overview.health.read","resource":"knowledge_space","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":262144,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"updateSettings","kfsOperationId":"updateKnowledgeSpaceProductSettings","method":"PATCH","path":"/knowledge-spaces/{id}/product-settings","action":"knowledge_spaces.settings.update","resource":"knowledge_space","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":65536,"productMaxResponseBytes":262144,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"listDocuments","kfsOperationId":"listDocuments","method":"GET","path":"/knowledge-spaces/{id}/documents","action":"documents.list","resource":"knowledge_space","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":16384,"productMaxResponseBytes":2097152,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"listLogicalDocuments","kfsOperationId":"listLogicalDocuments","method":"GET","path":"/knowledge-spaces/{id}/logical-documents","action":"logical_documents.list","resource":"knowledge_space","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":16384,"productMaxResponseBytes":2097152,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"getLogicalDocument","kfsOperationId":"getLogicalDocument","method":"GET","path":"/knowledge-spaces/{id}/logical-documents/{documentId}","action":"logical_documents.read","resource":"document","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"getDocument","kfsOperationId":"getDocument","method":"GET","path":"/knowledge-spaces/{id}/documents/{documentId}","action":"documents.read","resource":"document","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"getDocumentOutline","kfsOperationId":"getDocumentOutline","method":"GET","path":"/knowledge-spaces/{id}/documents/{documentId}/outline","action":"documents.outline.read","resource":"document","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":4194304,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"listDocumentRevisions","kfsOperationId":"listDocumentRevisions","method":"GET","path":"/knowledge-spaces/{id}/documents/{documentId}/revisions","action":"documents.revisions.list","resource":"document","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":16384,"productMaxResponseBytes":2097152,"kfsMaxResponseBytes":1048576}}, @@ -33,6 +35,19 @@ {"productOperationId":"updateSource","kfsOperationId":"updateKnowledgeSpaceSource","method":"PATCH","path":"/knowledge-spaces/{id}/sources/{sourceId}","action":"sources.update","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":262144,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"deleteSource","kfsOperationId":"requestSourceDeletion","method":"DELETE","path":"/knowledge-spaces/{id}/sources/{sourceId}","action":"sources.delete","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":32768,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"testSource","kfsOperationId":"testKnowledgeSpaceSource","method":"POST","path":"/knowledge-spaces/{id}/sources/{sourceId}/test","action":"sources.test","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":262144,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"syncSource","kfsOperationId":"createSourceSyncWorkflow","method":"POST","path":"/knowledge-spaces/{id}/sources/{sourceId}/sync","action":"source_workflows.sync.create","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":16384,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"listSourceProviders","kfsOperationId":"listSourceProviders","method":"GET","path":"/source-providers","action":"source_providers.list","resource":"namespace","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"createSourceConnection","kfsOperationId":"createSourceConnection","method":"POST","path":"/knowledge-spaces/{id}/source-connections","action":"source_connections.create","resource":"knowledge_space","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":262144,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"listSourceConnections","kfsOperationId":"listSourceConnections","method":"GET","path":"/knowledge-spaces/{id}/source-connections","action":"source_connections.list","resource":"knowledge_space","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":16384,"productMaxResponseBytes":1048576,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"refreshSourceConnection","kfsOperationId":"refreshSourceConnection","method":"POST","path":"/knowledge-spaces/{id}/source-connections/{connectionId}/refresh","action":"source_connections.refresh","resource":"knowledge_space","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":32768,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"previewSourceCrawl","kfsOperationId":"createSourceCrawlPreviewWorkflow","method":"POST","path":"/knowledge-spaces/{id}/sources/{sourceId}/crawl-preview","action":"source_workflows.preview.create","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":16384,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"getSourceSyncPolicy","kfsOperationId":"getSourceSyncPolicy","method":"GET","path":"/knowledge-spaces/{id}/sources/{sourceId}/sync-policy","action":"source_sync_policies.read","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":262144,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"updateSourceSyncPolicy","kfsOperationId":"putSourceSyncPolicy","method":"PUT","path":"/knowledge-spaces/{id}/sources/{sourceId}/sync-policy","action":"source_sync_policies.update","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":32768,"productMaxResponseBytes":262144,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"getSourceWorkflow","kfsOperationId":"getSourceWorkflow","method":"GET","path":"/knowledge-spaces/{id}/source-workflows/{runId}","action":"source_workflows.read","resource":"job","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"cancelSourceWorkflow","kfsOperationId":"cancelSourceWorkflow","method":"POST","path":"/knowledge-spaces/{id}/source-workflows/{runId}/cancel","action":"source_workflows.cancel","resource":"job","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":32768,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"retrySourceWorkflow","kfsOperationId":"retrySourceWorkflow","method":"POST","path":"/knowledge-spaces/{id}/source-workflows/{runId}/retry","action":"source_workflows.retry","resource":"job","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"listCrawlPreviewPages","kfsOperationId":"listCrawlPreviewPages","method":"GET","path":"/knowledge-spaces/{id}/source-workflows/{runId}/pages","action":"source_workflows.pages.list","resource":"job","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":16384,"productMaxResponseBytes":4194304,"kfsMaxResponseBytes":1048576}}, + {"productOperationId":"selectCrawlPreviewPages","kfsOperationId":"selectCrawlPreviewPages","method":"POST","path":"/knowledge-spaces/{id}/source-workflows/{runId}/selection","action":"source_workflows.selection.create","resource":"job","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":262144,"productMaxResponseBytes":524288,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"crawlSource","kfsOperationId":"crawlKnowledgeSpaceSource","method":"POST","path":"/knowledge-spaces/{id}/sources/{sourceId}/crawl","action":"sources.crawl","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":0,"productMaxResponseBytes":8388608,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"listSourcePages","kfsOperationId":"listKnowledgeSpaceSourcePages","method":"GET","path":"/knowledge-spaces/{id}/sources/{sourceId}/pages","action":"sources.pages.list","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":16384,"productMaxResponseBytes":4194304,"kfsMaxResponseBytes":1048576}}, {"productOperationId":"importSourcePages","kfsOperationId":"importKnowledgeSpaceSourcePages","method":"POST","path":"/knowledge-spaces/{id}/sources/{sourceId}/import","action":"sources.pages.import","resource":"source","transport":"json","stream":{"productKind":"json","kfsResponseKind":"buffered"},"limits":{"productMaxRequestBytes":1048576,"productMaxResponseBytes":4194304,"kfsMaxResponseBytes":1048576}}, diff --git a/api/services/feature_service.py b/api/services/feature_service.py index de72792f4d5..54f67dc1659 100644 --- a/api/services/feature_service.py +++ b/api/services/feature_service.py @@ -186,6 +186,8 @@ class SystemFeatureModel(FeatureResponseModel): enable_learn_app: bool = True enable_step_by_step_tour: bool = False rbac_enabled: bool = False + knowledge_fs_enabled: bool = False + knowledge_fs_upload_enabled: bool = False class FeatureService: @@ -289,6 +291,12 @@ class FeatureService: system_features.enable_learn_app = dify_config.ENABLE_LEARN_APP system_features.webapp_auth.allow_public_access = dify_config.WEBAPP_PUBLIC_ACCESS_ENABLED system_features.enable_step_by_step_tour = dify_config.ENABLE_STEP_BY_STEP_TOUR + system_features.knowledge_fs_enabled = dify_config.KNOWLEDGE_FS_ENABLED + system_features.knowledge_fs_upload_enabled = bool( + dify_config.KNOWLEDGE_FS_ENABLED + and dify_config.KNOWLEDGE_FS_DIRECT_ORIGIN + and dify_config.KNOWLEDGE_FS_DIRECT_UPLOAD_READY + ) @classmethod def _fulfill_trial_models_from_env(cls) -> list[str]: diff --git a/api/services/knowledge_fs/capability_broker.py b/api/services/knowledge_fs/capability_broker.py index c9c86438e98..54764e5995f 100644 --- a/api/services/knowledge_fs/capability_broker.py +++ b/api/services/knowledge_fs/capability_broker.py @@ -422,7 +422,9 @@ def _issue_request( trace_id: str, ) -> CapabilityIssueRequest: capability_operation = KNOWLEDGE_FS_CAPABILITY_OPERATIONS[capability_operation_id] - if capability_operation.resource_type == "knowledge_space": + if capability_operation.resource_type == "namespace": + resource = CapabilityResource(type="namespace", id=tenant_id) + elif capability_operation.resource_type == "knowledge_space": resource = CapabilityResource(type="knowledge_space", id=knowledge_space_id) elif capability_operation.resource_type in { "document", diff --git a/api/services/knowledge_fs/data_facade.py b/api/services/knowledge_fs/data_facade.py index 0d4073dc586..c4b40863a1b 100644 --- a/api/services/knowledge_fs/data_facade.py +++ b/api/services/knowledge_fs/data_facade.py @@ -18,6 +18,8 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSBulkDeletionAcceptedResponse, KnowledgeFSBulkDocumentDeletePayload, KnowledgeFSBulkJobResponse, + KnowledgeFSCrawlPreviewPageListResponse, + KnowledgeFSCrawlPreviewSelectionPayload, KnowledgeFSDocumentChunkListResponse, KnowledgeFSDocumentChunkResponse, KnowledgeFSDocumentCompilationJobResponse, @@ -31,6 +33,7 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSDocumentResponse, KnowledgeFSDocumentRevisionListResponse, KnowledgeFSDurableDeletionAcceptedResponse, + KnowledgeFSLogicalDocumentListResponse, KnowledgeFSLogicalDocumentResponse, KnowledgeFSOverviewBaseStatsResponse, KnowledgeFSOverviewHealthResponse, @@ -47,6 +50,10 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSSettingsPayload, KnowledgeFSSettingsResponse, KnowledgeFSSmallFileUploadResponse, + KnowledgeFSSourceConnectionCreatePayload, + KnowledgeFSSourceConnectionListResponse, + KnowledgeFSSourceConnectionRefreshPayload, + KnowledgeFSSourceConnectionResponse, KnowledgeFSSourceCrawlResponse, KnowledgeFSSourceCreatePayload, KnowledgeFSSourceCredentialTestResponse, @@ -57,8 +64,13 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSSourceImportResponse, KnowledgeFSSourceListResponse, KnowledgeFSSourcePagesResponse, + KnowledgeFSSourceProviderListResponse, KnowledgeFSSourceResponse, + KnowledgeFSSourceSyncPolicyPayload, + KnowledgeFSSourceSyncPolicyResponse, KnowledgeFSSourceUpdatePayload, + KnowledgeFSSourceWorkflowCancelPayload, + KnowledgeFSSourceWorkflowResponse, KnowledgeFSSpaceUpdatePayload, KnowledgeFSTraceEntryListResponse, KnowledgeFSTraceListResponse, @@ -194,6 +206,36 @@ class KnowledgeFSDataFacade: ) return KnowledgeFSDocumentListResponse.model_validate(raw) + def list_logical_documents( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + cursor: str | None, + ) -> KnowledgeFSLogicalDocumentListResponse: + raw = self._interactive( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="listLogicalDocuments", + query=(("cursor", cursor),) if cursor else (), + ) + return KnowledgeFSLogicalDocumentListResponse.model_validate(raw) + + def get_logical_document( + self, *, tenant_id: str, account_id: str, control_space_id: str, document_id: str + ) -> KnowledgeFSLogicalDocumentResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="getLogicalDocument", + resource_id=document_id, + path_parameters=(("documentId", document_id),), + ) + return KnowledgeFSLogicalDocumentResponse.model_validate(raw) + def create_document( self, *, @@ -631,6 +673,235 @@ class KnowledgeFSDataFacade: ) return KnowledgeFSSourceCredentialTestResponse.model_validate(raw) + def sync_source( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + source_id: str, + idempotency_key: str, + ) -> KnowledgeFSSourceWorkflowResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="syncSource", + resource_id=source_id, + path_parameters=(("sourceId", source_id),), + headers=(("Idempotency-Key", idempotency_key),), + ) + return KnowledgeFSSourceWorkflowResponse.model_validate(raw) + + def list_source_providers( + self, *, tenant_id: str, account_id: str, control_space_id: str + ) -> KnowledgeFSSourceProviderListResponse: + raw = self._interactive( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="listSourceProviders", + ) + return KnowledgeFSSourceProviderListResponse.model_validate(raw) + + def create_source_connection( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + payload: KnowledgeFSSourceConnectionCreatePayload, + ) -> KnowledgeFSSourceConnectionResponse: + raw = self._interactive( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="createSourceConnection", + payload=payload, + ) + return KnowledgeFSSourceConnectionResponse.model_validate(raw) + + def list_source_connections( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + cursor: str | None, + limit: int, + ) -> KnowledgeFSSourceConnectionListResponse: + query = (("limit", str(limit)),) + ((("cursor", cursor),) if cursor else ()) + raw = self._interactive( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="listSourceConnections", + query=query, + ) + return KnowledgeFSSourceConnectionListResponse.model_validate(raw) + + def refresh_source_connection( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + connection_id: str, + payload: KnowledgeFSSourceConnectionRefreshPayload, + ) -> KnowledgeFSSourceConnectionResponse: + raw = self._interactive( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="refreshSourceConnection", + payload=payload, + path_parameters=(("connectionId", connection_id),), + ) + return KnowledgeFSSourceConnectionResponse.model_validate(raw) + + def preview_source_crawl( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + source_id: str, + idempotency_key: str, + ) -> KnowledgeFSSourceWorkflowResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="previewSourceCrawl", + resource_id=source_id, + path_parameters=(("sourceId", source_id),), + headers=(("Idempotency-Key", idempotency_key),), + ) + return KnowledgeFSSourceWorkflowResponse.model_validate(raw) + + def get_source_sync_policy( + self, *, tenant_id: str, account_id: str, control_space_id: str, source_id: str + ) -> KnowledgeFSSourceSyncPolicyResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="getSourceSyncPolicy", + resource_id=source_id, + path_parameters=(("sourceId", source_id),), + ) + return KnowledgeFSSourceSyncPolicyResponse.model_validate(raw) + + def update_source_sync_policy( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + source_id: str, + payload: KnowledgeFSSourceSyncPolicyPayload, + ) -> KnowledgeFSSourceSyncPolicyResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="updateSourceSyncPolicy", + resource_id=source_id, + path_parameters=(("sourceId", source_id),), + payload=payload, + ) + return KnowledgeFSSourceSyncPolicyResponse.model_validate(raw) + + def get_source_workflow( + self, *, tenant_id: str, account_id: str, control_space_id: str, run_id: str + ) -> KnowledgeFSSourceWorkflowResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="getSourceWorkflow", + resource_id=run_id, + path_parameters=(("runId", run_id),), + ) + return KnowledgeFSSourceWorkflowResponse.model_validate(raw) + + def cancel_source_workflow( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + run_id: str, + payload: KnowledgeFSSourceWorkflowCancelPayload, + ) -> KnowledgeFSSourceWorkflowResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="cancelSourceWorkflow", + resource_id=run_id, + path_parameters=(("runId", run_id),), + payload=payload, + ) + return KnowledgeFSSourceWorkflowResponse.model_validate(raw) + + def retry_source_workflow( + self, *, tenant_id: str, account_id: str, control_space_id: str, run_id: str + ) -> KnowledgeFSSourceWorkflowResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="retrySourceWorkflow", + resource_id=run_id, + path_parameters=(("runId", run_id),), + ) + return KnowledgeFSSourceWorkflowResponse.model_validate(raw) + + def list_crawl_preview_pages( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + run_id: str, + cursor: str | None, + limit: int, + ) -> KnowledgeFSCrawlPreviewPageListResponse: + query = (("limit", str(limit)),) + ((("cursor", cursor),) if cursor else ()) + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="listCrawlPreviewPages", + resource_id=run_id, + path_parameters=(("runId", run_id),), + query=query, + ) + return KnowledgeFSCrawlPreviewPageListResponse.model_validate(raw) + + def select_crawl_preview_pages( + self, + *, + tenant_id: str, + account_id: str, + control_space_id: str, + run_id: str, + payload: KnowledgeFSCrawlPreviewSelectionPayload, + idempotency_key: str, + ) -> KnowledgeFSSourceWorkflowResponse: + raw = self._interactive_child( + tenant_id=tenant_id, + account_id=account_id, + control_space_id=control_space_id, + operation_id="selectCrawlPreviewPages", + resource_id=run_id, + path_parameters=(("runId", run_id),), + payload=payload, + headers=(("Idempotency-Key", idempotency_key),), + ) + return KnowledgeFSSourceWorkflowResponse.model_validate(raw) + def crawl_source( self, *, tenant_id: str, account_id: str, control_space_id: str, source_id: str ) -> KnowledgeFSSourceCrawlResponse: diff --git a/api/services/knowledge_fs/product_dto.py b/api/services/knowledge_fs/product_dto.py index 5b0c3e6889e..e63a1b1cd79 100644 --- a/api/services/knowledge_fs/product_dto.py +++ b/api/services/knowledge_fs/product_dto.py @@ -354,6 +354,7 @@ class KnowledgeFSOverviewStatsResponse(ResponseModel): class KnowledgeFSSpaceListItemResponse(ResponseModel): control_space_id: str + created_at: datetime state: KnowledgeFSControlSpaceState visibility: KnowledgeFSControlSpaceVisibility owner_account_id: str @@ -362,6 +363,7 @@ class KnowledgeFSSpaceListItemResponse(ResponseModel): permission_keys: list[KnowledgeFSProductPermission] technical_status: Literal["available", "not_ready", "unavailable"] technical_summary: KnowledgeFSTechnicalSummary | None = None + updated_at: datetime class KnowledgeFSSpaceListResponse(ResponseModel): @@ -372,8 +374,7 @@ class KnowledgeFSSpaceListResponse(ResponseModel): class KnowledgeFSSpaceDetailResponse(KnowledgeFSSpaceListItemResponse): - created_at: datetime - updated_at: datetime + pass class KnowledgeFSSpaceCreateResponse(ResponseModel): @@ -662,6 +663,11 @@ class KnowledgeFSLogicalDocumentResponse(ResponseModel): user_metadata: dict[str, object] = Field(validation_alias=AliasChoices("user_metadata", "userMetadata")) +class KnowledgeFSLogicalDocumentListResponse(ResponseModel): + data: list[KnowledgeFSLogicalDocumentResponse] = Field(validation_alias=AliasChoices("data", "items")) + next_cursor: str | None = Field(default=None, validation_alias=AliasChoices("next_cursor", "nextCursor")) + + class KnowledgeFSDocumentRevisionListResponse(ResponseModel): data: list[KnowledgeFSDocumentRevisionResponse] = Field(validation_alias=AliasChoices("data", "items")) next_cursor: str | None = Field(default=None, validation_alias=AliasChoices("next_cursor", "nextCursor")) @@ -963,6 +969,162 @@ class KnowledgeFSSourceCredentialTestResponse(ResponseModel): valid: bool +class KnowledgeFSSourceWorkflowResponse(ResponseModel): + canceled_at: datetime | None = Field(default=None, validation_alias=AliasChoices("canceled_at", "canceledAt")) + checkpoint: str + completed_at: datetime | None = Field(default=None, validation_alias=AliasChoices("completed_at", "completedAt")) + created_at: datetime = Field(validation_alias=AliasChoices("created_at", "createdAt")) + cursor: str | None = None + execution_attempts: int = Field(ge=0, validation_alias=AliasChoices("execution_attempts", "executionAttempts")) + id: str + knowledge_space_id: str = Field(validation_alias=AliasChoices("knowledge_space_id", "knowledgeSpaceId")) + kind: str + last_error_code: str | None = Field(default=None, validation_alias=AliasChoices("last_error_code", "lastErrorCode")) + max_execution_attempts: int = Field( + ge=1, validation_alias=AliasChoices("max_execution_attempts", "maxExecutionAttempts") + ) + progress_completed: int = Field(ge=0, validation_alias=AliasChoices("progress_completed", "progressCompleted")) + progress_failed: int = Field(ge=0, validation_alias=AliasChoices("progress_failed", "progressFailed")) + progress_skipped: int = Field(ge=0, validation_alias=AliasChoices("progress_skipped", "progressSkipped")) + progress_total: int | None = Field( + default=None, ge=0, validation_alias=AliasChoices("progress_total", "progressTotal") + ) + source_id: str | None = Field(default=None, validation_alias=AliasChoices("source_id", "sourceId")) + state: str + updated_at: datetime = Field(validation_alias=AliasChoices("updated_at", "updatedAt")) + + +class KnowledgeFSSourceProviderFieldResponse(ResponseModel): + description: str | None = None + format: Literal["password", "uri"] | None = None + name: str + required: bool + secret: bool + type: Literal["boolean", "integer", "string"] + + +class KnowledgeFSSourceProviderResponse(ResponseModel): + auth_kinds: list[Literal["api-key", "endpoint", "oauth2"]] = Field( + validation_alias=AliasChoices("auth_kinds", "authKinds") + ) + available: bool + capabilities: list[Literal["website-crawl", "online-document", "online-drive"]] + configuration: list[KnowledgeFSSourceProviderFieldResponse] + display_name: str = Field(validation_alias=AliasChoices("display_name", "displayName")) + id: str + unavailable_reason: str | None = Field( + default=None, validation_alias=AliasChoices("unavailable_reason", "unavailableReason") + ) + + +class KnowledgeFSSourceProviderListResponse(ResponseModel): + data: list[KnowledgeFSSourceProviderResponse] = Field(validation_alias=AliasChoices("data", "items")) + + +class KnowledgeFSSourceConnectionCreatePayload(BaseModel): + auth_kind: Literal["api-key", "endpoint"] = Field(alias="authKind") + configuration: dict[str, bool | int | str] = Field(default_factory=dict) + credentials: dict[str, object] + name: str = Field(min_length=1, max_length=160) + provider_id: str = Field(min_length=1, max_length=128, alias="providerId") + + model_config = ConfigDict(extra="forbid", validate_by_alias=True, validate_by_name=True) + + +class KnowledgeFSSourceConnectionResponse(ResponseModel): + auth_kind: Literal["api-key", "endpoint", "oauth2"] = Field(validation_alias=AliasChoices("auth_kind", "authKind")) + configuration: dict[str, bool | int | str] + created_at: datetime = Field(validation_alias=AliasChoices("created_at", "createdAt")) + error_code: str | None = Field(default=None, validation_alias=AliasChoices("error_code", "errorCode")) + expires_at: datetime | None = Field(default=None, validation_alias=AliasChoices("expires_at", "expiresAt")) + id: str + knowledge_space_id: str = Field(validation_alias=AliasChoices("knowledge_space_id", "knowledgeSpaceId")) + name: str + provider_id: str = Field(validation_alias=AliasChoices("provider_id", "providerId")) + scopes: list[str] + status: Literal["provisioning", "active", "expired", "error", "revoked"] + updated_at: datetime = Field(validation_alias=AliasChoices("updated_at", "updatedAt")) + version: int = Field(ge=1) + + +class KnowledgeFSSourceConnectionListQuery(BaseModel): + cursor: str | None = Field(default=None, min_length=1, max_length=4_096) + limit: int = Field(default=50, ge=1, le=200) + + model_config = ConfigDict(extra="forbid") + + +class KnowledgeFSSourceConnectionListResponse(ResponseModel): + data: list[KnowledgeFSSourceConnectionResponse] = Field(validation_alias=AliasChoices("data", "items")) + next_cursor: str | None = Field(default=None, validation_alias=AliasChoices("next_cursor", "nextCursor")) + + +class KnowledgeFSSourceConnectionRefreshPayload(BaseModel): + expected_version: int = Field(ge=1, alias="expectedVersion") + + model_config = ConfigDict(extra="forbid", validate_by_alias=True, validate_by_name=True) + + +class KnowledgeFSSourceSyncPolicyResponse(ResponseModel): + created_at: datetime = Field(validation_alias=AliasChoices("created_at", "createdAt")) + custom_interval_seconds: int | None = Field( + default=None, validation_alias=AliasChoices("custom_interval_seconds", "customIntervalSeconds") + ) + enabled: bool + expected_source_version: int = Field( + ge=1, validation_alias=AliasChoices("expected_source_version", "expectedSourceVersion") + ) + id: str + knowledge_space_id: str = Field(validation_alias=AliasChoices("knowledge_space_id", "knowledgeSpaceId")) + mode: Literal["provider", "manual", "interval", "custom"] + next_run_at: datetime | None = Field(default=None, validation_alias=AliasChoices("next_run_at", "nextRunAt")) + revision: int = Field(ge=1) + source_id: str = Field(validation_alias=AliasChoices("source_id", "sourceId")) + updated_at: datetime = Field(validation_alias=AliasChoices("updated_at", "updatedAt")) + + +class KnowledgeFSSourceSyncPolicyPayload(BaseModel): + custom_interval_seconds: int | None = Field(default=None, ge=3_600, le=2_592_000, alias="customIntervalSeconds") + enabled: bool + expected_revision: int = Field(ge=0, alias="expectedRevision") + expected_source_version: int = Field(ge=1, alias="expectedSourceVersion") + mode: Literal["provider", "manual", "interval", "custom"] + + model_config = ConfigDict(extra="forbid", validate_by_alias=True, validate_by_name=True) + + +class KnowledgeFSSourceWorkflowCancelPayload(BaseModel): + reason: str | None = Field(default=None, max_length=1_000) + + model_config = ConfigDict(extra="forbid") + + +class KnowledgeFSCrawlPreviewPageResponse(ResponseModel): + description: str | None = None + etag: str | None = None + page_id: str = Field(validation_alias=AliasChoices("page_id", "pageId")) + source_url: str = Field(validation_alias=AliasChoices("source_url", "sourceUrl")) + title: str | None = None + + +class KnowledgeFSCrawlPreviewPageListQuery(BaseModel): + cursor: str | None = Field(default=None, min_length=1, max_length=4_096) + limit: int = Field(default=50, ge=1, le=200) + + model_config = ConfigDict(extra="forbid") + + +class KnowledgeFSCrawlPreviewPageListResponse(ResponseModel): + data: list[KnowledgeFSCrawlPreviewPageResponse] = Field(validation_alias=AliasChoices("data", "items")) + next_cursor: str | None = Field(default=None, validation_alias=AliasChoices("next_cursor", "nextCursor")) + + +class KnowledgeFSCrawlPreviewSelectionPayload(BaseModel): + page_ids: list[str] = Field(min_length=1, max_length=200, alias="pageIds") + + model_config = ConfigDict(extra="forbid", validate_by_alias=True, validate_by_name=True) + + class KnowledgeFSCrawledPageResponse(ResponseModel): content: str description: str | None = None @@ -1468,6 +1630,9 @@ __all__ = [ "KnowledgeFSBulkDocumentDeletePayload", "KnowledgeFSBulkJobResponse", "KnowledgeFSCapabilityResponse", + "KnowledgeFSCrawlPreviewPageListQuery", + "KnowledgeFSCrawlPreviewPageListResponse", + "KnowledgeFSCrawlPreviewSelectionPayload", "KnowledgeFSCredentialCreatePayload", "KnowledgeFSCredentialCreateResponse", "KnowledgeFSCredentialItemResponse", @@ -1492,6 +1657,8 @@ __all__ = [ "KnowledgeFSIdempotencyHeader", "KnowledgeFSJWKResponse", "KnowledgeFSJWKSResponse", + "KnowledgeFSLogicalDocumentListResponse", + "KnowledgeFSLogicalDocumentResponse", "KnowledgeFSMemberBindingPayload", "KnowledgeFSMembersReplacePayload", "KnowledgeFSModelIntent", @@ -1532,6 +1699,11 @@ __all__ = [ "KnowledgeFSSettingsPayload", "KnowledgeFSSettingsResponse", "KnowledgeFSSmallFileUploadResponse", + "KnowledgeFSSourceConnectionCreatePayload", + "KnowledgeFSSourceConnectionListQuery", + "KnowledgeFSSourceConnectionListResponse", + "KnowledgeFSSourceConnectionRefreshPayload", + "KnowledgeFSSourceConnectionResponse", "KnowledgeFSSourceCrawlResponse", "KnowledgeFSSourceCreatePayload", "KnowledgeFSSourceCredentialTestResponse", @@ -1545,8 +1717,13 @@ __all__ = [ "KnowledgeFSSourceListResponse", "KnowledgeFSSourcePagesQuery", "KnowledgeFSSourcePagesResponse", + "KnowledgeFSSourceProviderListResponse", "KnowledgeFSSourceResponse", + "KnowledgeFSSourceSyncPolicyPayload", + "KnowledgeFSSourceSyncPolicyResponse", "KnowledgeFSSourceUpdatePayload", + "KnowledgeFSSourceWorkflowCancelPayload", + "KnowledgeFSSourceWorkflowResponse", "KnowledgeFSSpaceCreatePayload", "KnowledgeFSSpaceCreateResponse", "KnowledgeFSSpaceDetailResponse", diff --git a/api/services/knowledge_fs/product_operations.py b/api/services/knowledge_fs/product_operations.py index 09478711146..dcd91cfba55 100644 --- a/api/services/knowledge_fs/product_operations.py +++ b/api/services/knowledge_fs/product_operations.py @@ -233,6 +233,30 @@ KNOWLEDGE_FS_PRODUCT_OPERATIONS: Final[MappingProxyType[str, KnowledgeFSProductO max_response_bytes=2 * 1024 * 1024, stream_kind="json", ), + "listLogicalDocuments": _operation( + "GET", + "listLogicalDocuments", + KnowledgeFSProductPermission.READ, + "/knowledge-spaces/{id}/logical-documents", + "json", + resource_resolver="knowledge_space", + billing_cost=2, + max_request_bytes=16 * 1024, + max_response_bytes=2 * 1024 * 1024, + stream_kind="json", + ), + "getLogicalDocument": _operation( + "GET", + "getLogicalDocument", + KnowledgeFSProductPermission.READ, + "/knowledge-spaces/{id}/logical-documents/{documentId}", + "json", + resource_resolver="document", + billing_cost=2, + max_request_bytes=0, + max_response_bytes=512 * 1024, + stream_kind="json", + ), "createDocument": _operation( "POST", "uploadDocument", @@ -510,6 +534,165 @@ KNOWLEDGE_FS_PRODUCT_OPERATIONS: Final[MappingProxyType[str, KnowledgeFSProductO max_response_bytes=256 * 1024, stream_kind="json", ), + "syncSource": _operation( + "POST", + "createSourceSyncWorkflow", + KnowledgeFSProductPermission.DOCUMENT_WRITE, + "/knowledge-spaces/{id}/sources/{sourceId}/sync", + "json", + resource_resolver="source", + billing_cost=8, + max_request_bytes=16 * 1024, + max_response_bytes=512 * 1024, + stream_kind="json", + rate_limit_bucket="import", + ), + "listSourceProviders": _operation( + "GET", + "listSourceProviders", + KnowledgeFSProductPermission.READ, + "/source-providers", + "json", + resource_resolver="namespace", + billing_cost=1, + max_request_bytes=0, + max_response_bytes=512 * 1024, + stream_kind="json", + ), + "createSourceConnection": _operation( + "POST", + "createSourceConnection", + KnowledgeFSProductPermission.DOCUMENT_WRITE, + "/knowledge-spaces/{id}/source-connections", + "json", + resource_resolver="knowledge_space", + billing_cost=5, + max_request_bytes=256 * 1024, + max_response_bytes=512 * 1024, + stream_kind="json", + ), + "listSourceConnections": _operation( + "GET", + "listSourceConnections", + KnowledgeFSProductPermission.READ, + "/knowledge-spaces/{id}/source-connections", + "json", + resource_resolver="knowledge_space", + billing_cost=1, + max_request_bytes=16 * 1024, + max_response_bytes=1024 * 1024, + stream_kind="json", + ), + "refreshSourceConnection": _operation( + "POST", + "refreshSourceConnection", + KnowledgeFSProductPermission.DOCUMENT_WRITE, + "/knowledge-spaces/{id}/source-connections/{connectionId}/refresh", + "json", + resource_resolver="knowledge_space", + billing_cost=3, + max_request_bytes=32 * 1024, + max_response_bytes=512 * 1024, + stream_kind="json", + ), + "previewSourceCrawl": _operation( + "POST", + "createSourceCrawlPreviewWorkflow", + KnowledgeFSProductPermission.DOCUMENT_WRITE, + "/knowledge-spaces/{id}/sources/{sourceId}/crawl-preview", + "json", + resource_resolver="source", + billing_cost=8, + max_request_bytes=16 * 1024, + max_response_bytes=512 * 1024, + stream_kind="json", + rate_limit_bucket="import", + ), + "getSourceSyncPolicy": _operation( + "GET", + "getSourceSyncPolicy", + KnowledgeFSProductPermission.READ, + "/knowledge-spaces/{id}/sources/{sourceId}/sync-policy", + "json", + resource_resolver="source", + billing_cost=1, + max_request_bytes=0, + max_response_bytes=256 * 1024, + stream_kind="json", + ), + "updateSourceSyncPolicy": _operation( + "PUT", + "putSourceSyncPolicy", + KnowledgeFSProductPermission.DOCUMENT_WRITE, + "/knowledge-spaces/{id}/sources/{sourceId}/sync-policy", + "json", + resource_resolver="source", + billing_cost=3, + max_request_bytes=32 * 1024, + max_response_bytes=256 * 1024, + stream_kind="json", + ), + "getSourceWorkflow": _operation( + "GET", + "getSourceWorkflow", + KnowledgeFSProductPermission.READ, + "/knowledge-spaces/{id}/source-workflows/{runId}", + "json", + resource_resolver="job", + billing_cost=1, + max_request_bytes=0, + max_response_bytes=512 * 1024, + stream_kind="json", + ), + "cancelSourceWorkflow": _operation( + "POST", + "cancelSourceWorkflow", + KnowledgeFSProductPermission.DOCUMENT_WRITE, + "/knowledge-spaces/{id}/source-workflows/{runId}/cancel", + "json", + resource_resolver="job", + billing_cost=2, + max_request_bytes=32 * 1024, + max_response_bytes=512 * 1024, + stream_kind="json", + ), + "retrySourceWorkflow": _operation( + "POST", + "retrySourceWorkflow", + KnowledgeFSProductPermission.DOCUMENT_WRITE, + "/knowledge-spaces/{id}/source-workflows/{runId}/retry", + "json", + resource_resolver="job", + billing_cost=3, + max_request_bytes=0, + max_response_bytes=512 * 1024, + stream_kind="json", + ), + "listCrawlPreviewPages": _operation( + "GET", + "listCrawlPreviewPages", + KnowledgeFSProductPermission.READ, + "/knowledge-spaces/{id}/source-workflows/{runId}/pages", + "json", + resource_resolver="job", + billing_cost=1, + max_request_bytes=16 * 1024, + max_response_bytes=4 * 1024 * 1024, + stream_kind="json", + ), + "selectCrawlPreviewPages": _operation( + "POST", + "selectCrawlPreviewPages", + KnowledgeFSProductPermission.DOCUMENT_WRITE, + "/knowledge-spaces/{id}/source-workflows/{runId}/selection", + "json", + resource_resolver="job", + billing_cost=8, + max_request_bytes=256 * 1024, + max_response_bytes=512 * 1024, + stream_kind="json", + rate_limit_bucket="import", + ), "crawlSource": _operation( "POST", "crawlKnowledgeSpaceSource", diff --git a/api/services/knowledge_fs/product_remote.py b/api/services/knowledge_fs/product_remote.py index a73dc028778..94907cabe67 100644 --- a/api/services/knowledge_fs/product_remote.py +++ b/api/services/knowledge_fs/product_remote.py @@ -13,6 +13,10 @@ class KnowledgeFSProductRemoteError(RuntimeError): """KnowledgeFS could not provide an authoritative product response.""" +class KnowledgeFSProductResourceNotFoundError(KnowledgeFSProductRemoteError): + """KnowledgeFS authoritatively reported that an authorized child resource is absent.""" + + class KnowledgeFSOperationUnavailableError(RuntimeError): """The Dify/KFS/Capability operation manifests are not yet aligned.""" @@ -96,6 +100,7 @@ __all__ = [ "KnowledgeFSProductRemoteError", "KnowledgeFSProductRemotePort", "KnowledgeFSProductRequestRejectedError", + "KnowledgeFSProductResourceNotFoundError", "KnowledgeFSRemoteBinaryRequest", "KnowledgeFSRemoteJSONRequest", "UnavailableKnowledgeFSProductRemote", diff --git a/api/services/knowledge_fs/product_remote_http.py b/api/services/knowledge_fs/product_remote_http.py index b6452816eaa..fd1384dd902 100644 --- a/api/services/knowledge_fs/product_remote_http.py +++ b/api/services/knowledge_fs/product_remote_http.py @@ -20,6 +20,7 @@ from services.knowledge_fs.product_remote import ( KnowledgeFSOperationUnavailableError, KnowledgeFSProductRemoteError, KnowledgeFSProductRequestRejectedError, + KnowledgeFSProductResourceNotFoundError, KnowledgeFSRemoteBinaryRequest, KnowledgeFSRemoteJSONRequest, ) @@ -167,6 +168,8 @@ class HTTPKnowledgeFSProductRemoteClient: content_type = response.headers.get("content-type", "").partition(";")[0].strip().lower() if content_type != "application/json" and not content_type.endswith("+json"): raise KnowledgeFSProductRemoteError("KnowledgeFS returned an unsupported media type") + if response.status_code == HTTPStatus.NOT_FOUND: + raise KnowledgeFSProductResourceNotFoundError("KnowledgeFS resource was not found") if not HTTPStatus.OK <= response.status_code < HTTPStatus.MULTIPLE_CHOICES: raise KnowledgeFSProductRemoteError(f"KnowledgeFS returned HTTP {response.status_code}") try: @@ -234,9 +237,17 @@ class HTTPKnowledgeFSProductRemoteClient: except (ssrf_proxy.ResponseLimitError, httpx.RequestError, ToolSSRFError) as exc: raise KnowledgeFSProductRemoteError("KnowledgeFS request failed") from exc try: + if response.status_code == 409: + raise KnowledgeFSProductRequestRejectedError(status_code=409) + if response.status_code == 413: + raise KnowledgeFSProductRequestRejectedError(status_code=413) + if response.status_code == 422: + raise KnowledgeFSProductRequestRejectedError(status_code=422) content_type = response.headers.get("content-type", "").partition(";")[0].strip().lower() if content_type != "application/json" and not content_type.endswith("+json"): raise KnowledgeFSProductRemoteError("KnowledgeFS returned an unsupported media type") + if response.status_code == HTTPStatus.NOT_FOUND: + raise KnowledgeFSProductResourceNotFoundError("KnowledgeFS resource was not found") if not HTTPStatus.OK <= response.status_code < HTTPStatus.MULTIPLE_CHOICES: raise KnowledgeFSProductRemoteError(f"KnowledgeFS returned HTTP {response.status_code}") try: diff --git a/api/services/knowledge_fs/product_service.py b/api/services/knowledge_fs/product_service.py index 8e5d2199ea2..1dfd27b8faf 100644 --- a/api/services/knowledge_fs/product_service.py +++ b/api/services/knowledge_fs/product_service.py @@ -212,11 +212,7 @@ class KnowledgeFSProductService: trace_id=str(uuid.uuid4()), ) item = _list_item(space, summaries=summaries, permission_keys=authorized.permission_keys) - return KnowledgeFSSpaceDetailResponse( - **item.model_dump(), - created_at=space.created_at, - updated_at=space.updated_at, - ) + return KnowledgeFSSpaceDetailResponse(**item.model_dump()) def require_product_routes(self, *, tenant_id: str) -> None: self._cutover_gate.require_product_routes(tenant_id=tenant_id) @@ -308,6 +304,7 @@ def _list_item( technical_status = "available" return KnowledgeFSSpaceListItemResponse( control_space_id=space.id, + created_at=space.created_at, state=space.state, visibility=space.visibility, owner_account_id=space.owner_account_id, @@ -316,6 +313,7 @@ def _list_item( permission_keys=list(permission_keys), technical_status=technical_status, technical_summary=summary, + updated_at=space.updated_at, ) diff --git a/api/services/knowledge_fs_capability.py b/api/services/knowledge_fs_capability.py index fa8f7e3410c..5d5946bf847 100644 --- a/api/services/knowledge_fs_capability.py +++ b/api/services/knowledge_fs_capability.py @@ -388,6 +388,20 @@ KNOWLEDGE_FS_CAPABILITY_OPERATIONS: Final[Mapping[str, KnowledgeFSCapabilityOper "/knowledge-spaces/{id}/documents", "knowledge_space", ), + "listLogicalDocuments": KnowledgeFSCapabilityOperation( + "logical_documents.list", + _STANDARD_CALLERS, + "GET", + "/knowledge-spaces/{id}/logical-documents", + "knowledge_space", + ), + "getLogicalDocument": KnowledgeFSCapabilityOperation( + "logical_documents.read", + _STANDARD_CALLERS, + "GET", + "/knowledge-spaces/{id}/logical-documents/{documentId}", + "document", + ), "uploadDocument": KnowledgeFSCapabilityOperation( "documents.create", _STANDARD_CALLERS, @@ -533,6 +547,97 @@ KNOWLEDGE_FS_CAPABILITY_OPERATIONS: Final[Mapping[str, KnowledgeFSCapabilityOper "/knowledge-spaces/{id}/sources/{sourceId}/test", "source", ), + "createSourceSyncWorkflow": KnowledgeFSCapabilityOperation( + "source_workflows.sync.create", + _STANDARD_CALLERS, + "POST", + "/knowledge-spaces/{id}/sources/{sourceId}/sync", + "source", + ), + "listSourceProviders": KnowledgeFSCapabilityOperation( + "source_providers.list", + _STANDARD_CALLERS, + "GET", + "/source-providers", + "namespace", + ), + "createSourceConnection": KnowledgeFSCapabilityOperation( + "source_connections.create", + _STANDARD_CALLERS, + "POST", + "/knowledge-spaces/{id}/source-connections", + "knowledge_space", + ), + "listSourceConnections": KnowledgeFSCapabilityOperation( + "source_connections.list", + _STANDARD_CALLERS, + "GET", + "/knowledge-spaces/{id}/source-connections", + "knowledge_space", + ), + "refreshSourceConnection": KnowledgeFSCapabilityOperation( + "source_connections.refresh", + _STANDARD_CALLERS, + "POST", + "/knowledge-spaces/{id}/source-connections/{connectionId}/refresh", + "knowledge_space", + ), + "createSourceCrawlPreviewWorkflow": KnowledgeFSCapabilityOperation( + "source_workflows.preview.create", + _STANDARD_CALLERS, + "POST", + "/knowledge-spaces/{id}/sources/{sourceId}/crawl-preview", + "source", + ), + "getSourceSyncPolicy": KnowledgeFSCapabilityOperation( + "source_sync_policies.read", + _STANDARD_CALLERS, + "GET", + "/knowledge-spaces/{id}/sources/{sourceId}/sync-policy", + "source", + ), + "putSourceSyncPolicy": KnowledgeFSCapabilityOperation( + "source_sync_policies.update", + _STANDARD_CALLERS, + "PUT", + "/knowledge-spaces/{id}/sources/{sourceId}/sync-policy", + "source", + ), + "getSourceWorkflow": KnowledgeFSCapabilityOperation( + "source_workflows.read", + _STANDARD_CALLERS, + "GET", + "/knowledge-spaces/{id}/source-workflows/{runId}", + "job", + ), + "cancelSourceWorkflow": KnowledgeFSCapabilityOperation( + "source_workflows.cancel", + _STANDARD_CALLERS, + "POST", + "/knowledge-spaces/{id}/source-workflows/{runId}/cancel", + "job", + ), + "retrySourceWorkflow": KnowledgeFSCapabilityOperation( + "source_workflows.retry", + _STANDARD_CALLERS, + "POST", + "/knowledge-spaces/{id}/source-workflows/{runId}/retry", + "job", + ), + "listCrawlPreviewPages": KnowledgeFSCapabilityOperation( + "source_workflows.pages.list", + _STANDARD_CALLERS, + "GET", + "/knowledge-spaces/{id}/source-workflows/{runId}/pages", + "job", + ), + "selectCrawlPreviewPages": KnowledgeFSCapabilityOperation( + "source_workflows.selection.create", + _STANDARD_CALLERS, + "POST", + "/knowledge-spaces/{id}/source-workflows/{runId}/selection", + "job", + ), "crawlKnowledgeSpaceSource": KnowledgeFSCapabilityOperation( "sources.crawl", _STANDARD_CALLERS, diff --git a/api/tests/unit_tests/configs/test_knowledge_fs_config.py b/api/tests/unit_tests/configs/test_knowledge_fs_config.py index 2018617ef14..f0d89aaaf6d 100644 --- a/api/tests/unit_tests/configs/test_knowledge_fs_config.py +++ b/api/tests/unit_tests/configs/test_knowledge_fs_config.py @@ -17,6 +17,7 @@ _KNOWLEDGE_FS_DOCKER_VARIABLES = ( "KNOWLEDGE_FS_ENABLED", "KNOWLEDGE_FS_BASE_URL", "KNOWLEDGE_FS_DIRECT_ORIGIN", + "KNOWLEDGE_FS_DIRECT_UPLOAD_READY", "KNOWLEDGE_FS_LIFECYCLE_WORKER_ENABLED", "KNOWLEDGE_FS_INTEGRATED_PROVISION_READY", "KNOWLEDGE_FS_LEGACY_ACL_FREEZE_READY", @@ -68,6 +69,7 @@ def test_knowledge_fs_lifecycle_worker_is_disabled_by_default() -> None: assert config.KNOWLEDGE_FS_INTEGRATED_PROVISION_READY is False assert config.KNOWLEDGE_FS_LEGACY_ACL_FREEZE_READY is False assert config.KNOWLEDGE_FS_CAPABILITY_V2_ENABLED is False + assert config.KNOWLEDGE_FS_DIRECT_UPLOAD_READY is False def test_capability_v2_requires_private_signing_configuration_when_enabled() -> None: diff --git a/api/tests/unit_tests/controllers/inner_api/plugin/test_plugin_wraps.py b/api/tests/unit_tests/controllers/inner_api/plugin/test_plugin_wraps.py index 5faf7828f57..c1cd3b551a6 100644 --- a/api/tests/unit_tests/controllers/inner_api/plugin/test_plugin_wraps.py +++ b/api/tests/unit_tests/controllers/inner_api/plugin/test_plugin_wraps.py @@ -25,6 +25,8 @@ from models.base import TypeBase from models.enums import EndUserType from models.model import DefaultEndUserSessionID, EndUser +_USER_UUID = "00000000-0000-4000-8000-000000000001" + @pytest.fixture def sqlite_plugin_engine( @@ -101,14 +103,14 @@ class TestGetUser: """Test returning existing user when found by ID""" _persist_end_user( sqlite_plugin_engine, - user_id="user123", + user_id=_USER_UUID, session_id="existing-session", ) with app.app_context(): - result = get_user("tenant123", "user123") + result = get_user("tenant123", _USER_UUID) - assert result.id == "user123" + assert result.id == _USER_UUID assert result.tenant_id == "tenant123" def test_should_not_resolve_non_anonymous_users_across_tenants( @@ -158,6 +160,39 @@ class TestGetUser: users = session.scalars(select(EndUser)).all() assert [user.id for user in users] == ["persisted-user-id"] + def test_should_skip_uuid_id_lookup_for_text_session_id( + self, + sqlite_plugin_engine: Engine, + app: Flask, + ): + """Service actor names must not be compared with the UUID primary key.""" + _persist_end_user( + sqlite_plugin_engine, + user_id="persisted-user-id", + session_id="knowledge-fs", + ) + statements: list[str] = [] + + def _record_statement( + _connection: object, + _cursor: object, + statement: str, + _parameters: object, + _context: object, + _executemany: bool, + ) -> None: + statements.append(statement) + + event.listen(sqlite_plugin_engine, "before_cursor_execute", _record_statement) + try: + with app.app_context(): + result = get_user("tenant123", "knowledge-fs") + finally: + event.remove(sqlite_plugin_engine, "before_cursor_execute", _record_statement) + + assert result.id == "persisted-user-id" + assert not any("WHERE end_users.id =" in statement for statement in statements) + def test_should_return_existing_anonymous_user_by_session_id( self, sqlite_plugin_engine: Engine, @@ -244,17 +279,17 @@ class TestGetUserTenant: _persist_tenant(sqlite_plugin_engine) _persist_end_user( sqlite_plugin_engine, - user_id="user456", + user_id=_USER_UUID, session_id="user-session", ) - with app.test_request_context(json={"tenant_id": "tenant123", "user_id": "user456"}): + with app.test_request_context(json={"tenant_id": "tenant123", "user_id": _USER_UUID}): monkeypatch.setattr(app, "login_manager", MagicMock(), raising=False) with patch("controllers.inner_api.plugin.wraps.user_logged_in"): result = protected_view() assert result["tenant"].id == "tenant123" - assert result["user"].id == "user456" + assert result["user"].id == _USER_UUID def test_should_raise_error_when_tenant_id_missing(self, app: Flask): """Test that Pydantic ValidationError is raised when tenant_id is missing from payload""" diff --git a/api/tests/unit_tests/controllers/test_knowledge_fs_product_controllers.py b/api/tests/unit_tests/controllers/test_knowledge_fs_product_controllers.py index 3f69d1c603d..f467cf6faa8 100644 --- a/api/tests/unit_tests/controllers/test_knowledge_fs_product_controllers.py +++ b/api/tests/unit_tests/controllers/test_knowledge_fs_product_controllers.py @@ -46,7 +46,20 @@ def test_console_and_service_api_routes_are_registered() -> None: "/knowledge-fs/spaces//credentials", "/knowledge-fs/spaces//settings", "/knowledge-fs/spaces//documents", + "/knowledge-fs/spaces//logical-documents", + "/knowledge-fs/spaces//logical-documents/", "/knowledge-fs/spaces//sources", + "/knowledge-fs/spaces//source-connections", + ("/knowledge-fs/spaces//source-connections//refresh"), + "/knowledge-fs/spaces//sources//sync", + "/knowledge-fs/spaces//sources//crawl-preview", + "/knowledge-fs/spaces//sources//sync-policy", + "/knowledge-fs/spaces//source-workflows/", + "/knowledge-fs/spaces//source-workflows//cancel", + "/knowledge-fs/spaces//source-workflows//retry", + "/knowledge-fs/spaces//source-workflows//pages", + "/knowledge-fs/spaces//source-workflows//selection", + "/knowledge-fs/spaces//source-providers", "/knowledge-fs/spaces//queries", "/knowledge-fs/spaces//research-tasks", "/knowledge-fs/spaces//traces", @@ -114,6 +127,18 @@ def test_knowledge_fs_request_and_response_schemas_are_registered() -> None: "KnowledgeFSStreamCapabilityResponse", "KnowledgeFSJWKSResponse", "KnowledgeFSSmallFileUploadResponse", + "KnowledgeFSCrawlPreviewPageListQuery", + "KnowledgeFSCrawlPreviewPageListResponse", + "KnowledgeFSCrawlPreviewSelectionPayload", + "KnowledgeFSSourceConnectionCreatePayload", + "KnowledgeFSSourceConnectionListQuery", + "KnowledgeFSSourceConnectionListResponse", + "KnowledgeFSSourceConnectionRefreshPayload", + "KnowledgeFSSourceProviderListResponse", + "KnowledgeFSSourceSyncPolicyPayload", + "KnowledgeFSSourceSyncPolicyResponse", + "KnowledgeFSSourceWorkflowCancelPayload", + "KnowledgeFSSourceWorkflowResponse", }.issubset(console_ns.models) assert { "KnowledgeFSDocumentCreatePayload", @@ -524,6 +549,7 @@ def test_upload_and_task_stream_capabilities_use_direct_operation_admission( runtime = SimpleNamespace(direct_operation_admission=DirectAdmission()) monkeypatch.setattr(console_resources.dify_config, "KNOWLEDGE_FS_DIRECT_ORIGIN", "https://kfs.test") + monkeypatch.setattr(console_resources.dify_config, "KNOWLEDGE_FS_DIRECT_UPLOAD_READY", True) monkeypatch.setattr(console_resources, "_actor", lambda: ("account-1", "tenant-1")) monkeypatch.setattr(console_resources, "_console_services", lambda: runtime) app = Flask(__name__) diff --git a/api/tests/unit_tests/controllers/test_knowledge_fs_resource_delegation.py b/api/tests/unit_tests/controllers/test_knowledge_fs_resource_delegation.py index 41404cf4d19..7368172af93 100644 --- a/api/tests/unit_tests/controllers/test_knowledge_fs_resource_delegation.py +++ b/api/tests/unit_tests/controllers/test_knowledge_fs_resource_delegation.py @@ -384,11 +384,11 @@ _CONSOLE_DELEGATION_CASES = ( {"control_space_id": "space-1", "source_id": "source-1"}, ), ( - "KnowledgeFSSpaceSourceCrawlApi", + "KnowledgeFSSpaceSourceSyncApi", "post", ("space-1", "source-1"), "facade", - "crawl_source", + "sync_source", {"control_space_id": "space-1", "source_id": "source-1"}, ), ( @@ -987,6 +987,7 @@ def test_console_direct_capabilities_bind_the_authorized_resource(monkeypatch: p ] ) monkeypatch.setattr(console_resources.dify_config, "KNOWLEDGE_FS_DIRECT_ORIGIN", "https://kfs.example/") + monkeypatch.setattr(console_resources.dify_config, "KNOWLEDGE_FS_DIRECT_UPLOAD_READY", True) monkeypatch.setattr(console_resources, "_actor", lambda: ("account-1", "tenant-1")) monkeypatch.setattr( console_resources, @@ -1066,6 +1067,16 @@ def test_direct_routes_fail_before_admission_when_origin_is_unconfigured( _invoke(resource_module, class_name, "post", "resource-1") +def test_console_upload_capability_fails_before_admission_until_upload_is_verified( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr(console_resources.dify_config, "KNOWLEDGE_FS_DIRECT_ORIGIN", "https://kfs.example") + monkeypatch.setattr(console_resources.dify_config, "KNOWLEDGE_FS_DIRECT_UPLOAD_READY", False) + + with pytest.raises(KnowledgeFSOperationUnavailableError, match="direct upload"): + _invoke(console_resources, "KnowledgeFSSpaceUploadCapabilitiesApi", "post", "space-1") + + def test_console_resource_helpers_validate_feature_payload_headers_and_query_pairs( monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -1194,7 +1205,10 @@ def test_console_error_adapter_maps_every_domain_boundary_to_the_stable_http_con from services.knowledge_fs.control_plane_service import KnowledgeFSControlPlaneInvariantError from services.knowledge_fs.credential_service import KnowledgeFSCredentialPolicyError from services.knowledge_fs.product_authorization import KnowledgeFSProductNotFoundError - from services.knowledge_fs.product_remote import KnowledgeFSProductRemoteError + from services.knowledge_fs.product_remote import ( + KnowledgeFSProductRemoteError, + KnowledgeFSProductResourceNotFoundError, + ) from services.knowledge_fs_capability import KnowledgeFSCapabilityConfigurationError with pytest.raises(ValidationError) as raised_validation: @@ -1202,6 +1216,7 @@ def test_console_error_adapter_maps_every_domain_boundary_to_the_stable_http_con validation_error = raised_validation.value mappings = ( (KnowledgeFSProductNotFoundError("hidden"), KnowledgeFSSpaceNotFoundHTTPError), + (KnowledgeFSProductResourceNotFoundError("missing child"), NotFound), (KnowledgeFSOperationUnavailableError("manifest mismatch"), KnowledgeFSOperationUnavailableHTTPError), (KnowledgeFSProductRemoteError("upstream unavailable"), KnowledgeFSUpstreamUnavailableHTTPError), (KnowledgeFSAppBindingManagementError("invalid binding"), KnowledgeFSInvalidRequestHTTPError), @@ -1223,7 +1238,10 @@ def test_service_error_adapter_maps_every_domain_boundary_to_the_stable_http_con from pydantic import ValidationError from services.knowledge_fs.credential_service import KnowledgeFSCredentialValidationError - from services.knowledge_fs.product_remote import KnowledgeFSProductRemoteError + from services.knowledge_fs.product_remote import ( + KnowledgeFSProductRemoteError, + KnowledgeFSProductResourceNotFoundError, + ) with pytest.raises(ValidationError) as raised_validation: KnowledgeFSQueryCreatePayload.model_validate({"query": ""}) @@ -1231,6 +1249,7 @@ def test_service_error_adapter_maps_every_domain_boundary_to_the_stable_http_con mappings = ( (KnowledgeFSCredentialValidationError("revoked"), KnowledgeFSInvalidCredentialHTTPError), (KnowledgeFSOperationUnavailableError("manifest mismatch"), KnowledgeFSServiceOperationUnavailableHTTPError), + (KnowledgeFSProductResourceNotFoundError("missing child"), NotFound), (KnowledgeFSProductRemoteError("upstream unavailable"), KnowledgeFSServiceUpstreamUnavailableHTTPError), (validation_error, KnowledgeFSServiceInvalidRequestHTTPError), ) diff --git a/api/tests/unit_tests/extensions/test_ext_blueprints_cors.py b/api/tests/unit_tests/extensions/test_ext_blueprints_cors.py new file mode 100644 index 00000000000..47b30bcf087 --- /dev/null +++ b/api/tests/unit_tests/extensions/test_ext_blueprints_cors.py @@ -0,0 +1,36 @@ +"""Regression coverage for authenticated browser CORS headers.""" + +from flask import Blueprint, Flask + +from extensions.ext_blueprints import AUTHENTICATED_HEADERS, _apply_cors_once + + +def test_authenticated_cors_allows_request_metadata_headers() -> None: + app = Flask(__name__) + blueprint = Blueprint("cors_probe", __name__, url_prefix="/console/api") + + @blueprint.post("/probe") + def probe() -> tuple[str, int]: + return "", 204 + + _apply_cors_once( + blueprint, + resources={r"/*": {"origins": ["http://localhost:3000"]}}, + supports_credentials=True, + allow_headers=list(AUTHENTICATED_HEADERS), + methods=["POST", "OPTIONS"], + ) + app.register_blueprint(blueprint) + + response = app.test_client().options( + "/console/api/probe", + headers={ + "Access-Control-Request-Headers": "Idempotency-Key, X-Request-ID", + "Access-Control-Request-Method": "POST", + "Origin": "http://localhost:3000", + }, + ) + + allowed_headers = response.headers.get("Access-Control-Allow-Headers", "").lower() + assert "idempotency-key" in allowed_headers + assert "x-request-id" in allowed_headers diff --git a/api/tests/unit_tests/services/test_feature_service_knowledge_fs.py b/api/tests/unit_tests/services/test_feature_service_knowledge_fs.py new file mode 100644 index 00000000000..bb9e9705799 --- /dev/null +++ b/api/tests/unit_tests/services/test_feature_service_knowledge_fs.py @@ -0,0 +1,36 @@ +import pytest + +from services import feature_service as feature_service_module +from services.feature_service import FeatureService + + +@pytest.mark.parametrize( + ("enabled", "direct_origin", "direct_upload_ready", "upload_enabled"), + [ + (True, "https://uploads.knowledge-fs.test", True, True), + (True, "https://uploads.knowledge-fs.test", False, False), + (True, None, True, False), + (False, "https://uploads.knowledge-fs.test", True, False), + ], +) +def test_get_system_features_reads_knowledge_fs_availability( + monkeypatch: pytest.MonkeyPatch, + enabled: bool, + direct_origin: str | None, + direct_upload_ready: bool, + upload_enabled: bool, +) -> None: + monkeypatch.setattr(feature_service_module.dify_config, "KNOWLEDGE_FS_ENABLED", enabled) + monkeypatch.setattr(feature_service_module.dify_config, "KNOWLEDGE_FS_DIRECT_ORIGIN", direct_origin) + monkeypatch.setattr( + feature_service_module.dify_config, + "KNOWLEDGE_FS_DIRECT_UPLOAD_READY", + direct_upload_ready, + ) + + result = FeatureService.get_system_features() + + assert result.knowledge_fs_enabled is enabled + assert result.knowledge_fs_upload_enabled is upload_enabled + assert result.model_dump()["knowledge_fs_enabled"] is enabled + assert result.model_dump()["knowledge_fs_upload_enabled"] is upload_enabled diff --git a/api/tests/unit_tests/services/test_knowledge_fs_data_facade.py b/api/tests/unit_tests/services/test_knowledge_fs_data_facade.py index 2ff169c1d17..52a43cb4b99 100644 --- a/api/tests/unit_tests/services/test_knowledge_fs_data_facade.py +++ b/api/tests/unit_tests/services/test_knowledge_fs_data_facade.py @@ -604,6 +604,20 @@ def test_advanced_facade_binds_child_resources_parent_space_and_idempotency() -> ("get_overview_inventory", "KnowledgeFSOverviewInventoryResponse", "getOverviewInventory", {}, None), ("get_overview_health", "KnowledgeFSOverviewHealthResponse", "getOverviewHealth", {}, None), ("list_documents", "KnowledgeFSDocumentListResponse", "listDocuments", {"cursor": "cursor-1"}, None), + ( + "list_logical_documents", + "KnowledgeFSLogicalDocumentListResponse", + "listLogicalDocuments", + {"cursor": "cursor-1"}, + None, + ), + ( + "get_logical_document", + "KnowledgeFSLogicalDocumentResponse", + "getLogicalDocument", + {"document_id": "document-1"}, + "document-1", + ), ("get_document", "KnowledgeFSDocumentResponse", "getDocument", {"document_id": "document-1"}, "document-1"), ( "get_document_outline", @@ -703,6 +717,97 @@ def test_advanced_facade_binds_child_resources_parent_space_and_idempotency() -> }, "source-1", ), + ( + "sync_source", + "KnowledgeFSSourceWorkflowResponse", + "syncSource", + {"source_id": "source-1", "idempotency_key": "sync-source-once"}, + "source-1", + ), + ( + "list_source_providers", + "KnowledgeFSSourceProviderListResponse", + "listSourceProviders", + {}, + None, + ), + ( + "create_source_connection", + "KnowledgeFSSourceConnectionResponse", + "createSourceConnection", + {"payload": MagicMock()}, + None, + ), + ( + "list_source_connections", + "KnowledgeFSSourceConnectionListResponse", + "listSourceConnections", + {"cursor": "cursor-1", "limit": 25}, + None, + ), + ( + "refresh_source_connection", + "KnowledgeFSSourceConnectionResponse", + "refreshSourceConnection", + {"connection_id": "connection-1", "payload": MagicMock()}, + None, + ), + ( + "preview_source_crawl", + "KnowledgeFSSourceWorkflowResponse", + "previewSourceCrawl", + {"source_id": "source-1", "idempotency_key": "preview-source-once"}, + "source-1", + ), + ( + "get_source_sync_policy", + "KnowledgeFSSourceSyncPolicyResponse", + "getSourceSyncPolicy", + {"source_id": "source-1"}, + "source-1", + ), + ( + "update_source_sync_policy", + "KnowledgeFSSourceSyncPolicyResponse", + "updateSourceSyncPolicy", + {"source_id": "source-1", "payload": MagicMock()}, + "source-1", + ), + ( + "get_source_workflow", + "KnowledgeFSSourceWorkflowResponse", + "getSourceWorkflow", + {"run_id": "run-1"}, + "run-1", + ), + ( + "cancel_source_workflow", + "KnowledgeFSSourceWorkflowResponse", + "cancelSourceWorkflow", + {"run_id": "run-1", "payload": MagicMock()}, + "run-1", + ), + ( + "retry_source_workflow", + "KnowledgeFSSourceWorkflowResponse", + "retrySourceWorkflow", + {"run_id": "run-1"}, + "run-1", + ), + ( + "list_crawl_preview_pages", + "KnowledgeFSCrawlPreviewPageListResponse", + "listCrawlPreviewPages", + {"run_id": "run-1", "cursor": "cursor-1", "limit": 25}, + "run-1", + ), + ( + "select_crawl_preview_pages", + "KnowledgeFSSourceWorkflowResponse", + "selectCrawlPreviewPages", + {"run_id": "run-1", "payload": MagicMock(), "idempotency_key": "selection-once"}, + "run-1", + ), ( "crawl_source", "KnowledgeFSSourceCrawlResponse", diff --git a/api/tests/unit_tests/services/test_knowledge_fs_product_operations.py b/api/tests/unit_tests/services/test_knowledge_fs_product_operations.py index 66243612121..e0f663b3561 100644 --- a/api/tests/unit_tests/services/test_knowledge_fs_product_operations.py +++ b/api/tests/unit_tests/services/test_knowledge_fs_product_operations.py @@ -29,11 +29,13 @@ def test_ready_product_operations_exactly_match_capability_method_path_and_actio "cancelBackgroundTask", "cancelCompilationJob", "cancelResearchTask", + "cancelSourceWorkflow", "completeUploadSession", "crawlSource", "createQuery", "createResearchTask", "createSource", + "createSourceConnection", "createUploadSession", "deleteDocument", "deleteSource", @@ -42,6 +44,7 @@ def test_ready_product_operations_exactly_match_capability_method_path_and_actio "getDocument", "getDocumentChunk", "getDocumentOutline", + "getLogicalDocument", "getOverviewHealth", "getOverviewInventory", "getOverviewQueryOutcomes", @@ -49,6 +52,8 @@ def test_ready_product_operations_exactly_match_capability_method_path_and_actio "getResearchTask", "getSettings", "getSource", + "getSourceSyncPolicy", + "getSourceWorkflow", "getSpace", "getTrace", "importSourceFiles", @@ -56,26 +61,36 @@ def test_ready_product_operations_exactly_match_capability_method_path_and_actio "listDocumentChunks", "listDocumentRevisions", "listDocuments", + "listLogicalDocuments", "listBackgroundTasks", + "listCrawlPreviewPages", "listResearchTaskPartials", "listResearchTasks", "listSources", + "listSourceConnections", "listSourceFiles", "listSourcePages", + "listSourceProviders", "listTraceConflicts", "listTraceEvidence", "listTraceMissing", "listTraces", "planResearchTask", "presignUploadSessionPart", + "previewSourceCrawl", "reindexDocuments", + "refreshSourceConnection", "retryBackgroundTask", "retryCompilationJob", + "retrySourceWorkflow", + "selectCrawlPreviewPages", "streamResearchTask", + "syncSource", "testSource", "updateDocumentMetadata", "updateSettings", "updateSource", + "updateSourceSyncPolicy", "updateSpace", "uploadSmallFile", } diff --git a/api/tests/unit_tests/services/test_knowledge_fs_product_remote_http.py b/api/tests/unit_tests/services/test_knowledge_fs_product_remote_http.py index 0504e8c1c0d..6ca6fd2352a 100644 --- a/api/tests/unit_tests/services/test_knowledge_fs_product_remote_http.py +++ b/api/tests/unit_tests/services/test_knowledge_fs_product_remote_http.py @@ -9,6 +9,7 @@ from services.knowledge_fs.product_remote import ( KnowledgeFSOperationUnavailableError, KnowledgeFSProductRemoteError, KnowledgeFSProductRequestRejectedError, + KnowledgeFSProductResourceNotFoundError, KnowledgeFSRemoteBinaryRequest, KnowledgeFSRemoteJSONRequest, ) @@ -520,17 +521,22 @@ def test_binary_remote_closes_and_maps_all_upstream_response_failures( @pytest.mark.parametrize( - ("status_code", "content_type", "body"), + ("status_code", "content_type", "body", "error_type", "expected_status"), [ - (500, "application/json", b"{}"), - (200, "text/plain", b"ok"), - (200, "application/json", b"{"), + (409, "application/json", b"{}", KnowledgeFSProductRequestRejectedError, 409), + (413, "application/json", b"{}", KnowledgeFSProductRequestRejectedError, 413), + (422, "application/json", b"{}", KnowledgeFSProductRequestRejectedError, 422), + (500, "application/json", b"{}", KnowledgeFSProductRemoteError, None), + (200, "text/plain", b"ok", KnowledgeFSProductRemoteError, None), + (200, "application/json", b"{", KnowledgeFSProductRemoteError, None), ], ) def test_json_remote_closes_and_maps_upstream_response_failures( status_code: int, content_type: str, body: bytes, + error_type: type[Exception], + expected_status: int | None, monkeypatch: pytest.MonkeyPatch, ) -> None: response = httpx.Response(status_code, content=body, headers={"Content-Type": content_type}) @@ -538,7 +544,28 @@ def test_json_remote_closes_and_maps_upstream_response_failures( monkeypatch.setattr(ssrf_proxy, "buffer_response", lambda buffered, **_: buffered) client = HTTPKnowledgeFSProductRemoteClient(base_url="https://knowledge-fs.test", timeout_seconds=3) - with pytest.raises(KnowledgeFSProductRemoteError): + with pytest.raises(error_type) as raised: + client.execute_json(_json_request()) + + if expected_status is not None: + assert isinstance(raised.value, KnowledgeFSProductRequestRejectedError) + assert raised.value.status_code == expected_status + assert response.is_closed + + +def test_json_remote_preserves_authoritative_resource_not_found( + monkeypatch: pytest.MonkeyPatch, +) -> None: + response = httpx.Response( + 404, + json={"error": "Source sync policy not found"}, + headers={"Content-Type": "application/json"}, + ) + monkeypatch.setattr(ssrf_proxy, "make_request", lambda **_: response) + monkeypatch.setattr(ssrf_proxy, "buffer_response", lambda buffered, **_: buffered) + client = HTTPKnowledgeFSProductRemoteClient(base_url="https://knowledge-fs.test", timeout_seconds=3) + + with pytest.raises(KnowledgeFSProductResourceNotFoundError): client.execute_json(_json_request()) assert response.is_closed diff --git a/docker/envs/core-services/api.env.example b/docker/envs/core-services/api.env.example index eaba07b617f..b3f715780e2 100644 --- a/docker/envs/core-services/api.env.example +++ b/docker/envs/core-services/api.env.example @@ -15,13 +15,14 @@ KNOWLEDGE_FS_ENABLED=${KNOWLEDGE_FS_ENABLED:-false} # Production deployments require HTTPS; plain HTTP is limited to non-production or loopback. KNOWLEDGE_FS_BASE_URL= KNOWLEDGE_FS_DIRECT_ORIGIN= +# Set true only after the KnowledgeFS direct-upload service and browser origins below are verified. +KNOWLEDGE_FS_DIRECT_UPLOAD_READY=false KNOWLEDGE_FS_LIFECYCLE_WORKER_ENABLED=false KNOWLEDGE_FS_INTEGRATED_PROVISION_READY=false KNOWLEDGE_FS_LEGACY_ACL_FREEZE_READY=false KNOWLEDGE_FS_LIFECYCLE_POLL_INTERVAL_SECONDS=15 KNOWLEDGE_FS_LIFECYCLE_LEASE_SECONDS=60 KNOWLEDGE_FS_LIFECYCLE_BATCH_SIZE=25 -# Legacy rollback-only HMAC; leave blank when Capability v2 is selected. KNOWLEDGE_FS_CAPABILITY_V2_ENABLED=false KNOWLEDGE_FS_CAPABILITY_V2_SIGNING_KID= KNOWLEDGE_FS_CAPABILITY_V2_PRIVATE_KEY_PEM= diff --git a/docker/envs/core-services/knowledge-fs.env.example b/docker/envs/core-services/knowledge-fs.env.example index d4f39d8f9da..56416d95ed3 100644 --- a/docker/envs/core-services/knowledge-fs.env.example +++ b/docker/envs/core-services/knowledge-fs.env.example @@ -19,6 +19,11 @@ KNOWLEDGE_DOCUMENT_COMPILATION_RUNTIME=on KNOWLEDGE_FS_CAPABILITY_V2_ENABLED=false KNOWLEDGE_FS_CAPABILITY_V2_PUBLIC_JWKS= +# Direct upload remains hidden in Dify until this service is enabled, its allowed browser origins +# are verified, and the API service sets KNOWLEDGE_FS_DIRECT_UPLOAD_READY=true. +KNOWLEDGE_DIRECT_UPLOAD_ENABLED=false +KNOWLEDGE_DIRECT_UPLOAD_ALLOWED_ORIGINS= + # Complex-document parser reachable from the default Compose network or an external endpoint. # Leave both values blank only when PDF/Office parsing is intentionally unavailable. UNSTRUCTURED_API_URL= diff --git a/knowledge-fs/apps/api/src/auth-options.test.ts b/knowledge-fs/apps/api/src/auth-options.test.ts index b7a26082a61..6fd067cf123 100644 --- a/knowledge-fs/apps/api/src/auth-options.test.ts +++ b/knowledge-fs/apps/api/src/auth-options.test.ts @@ -1,8 +1,57 @@ +import { createHmac } from "node:crypto"; import { describe, expect, it } from "vitest"; import { createApiAuthVerifier } from "./auth-options"; describe("createApiAuthVerifier", () => { + it.each(["development", "production"])( + "accepts Dify-issued workspace JWTs in %s mode", + async (nodeEnvironment) => { + const secret = "test-secret-with-at-least-32-bytes"; + const issuedAt = Math.floor(Date.now() / 1_000); + const token = signJwt( + { + caller_kind: "interactive", + scopes: ["knowledge-spaces:write"], + tenant_id: "tenant-1", + aud: "knowledge-fs", + exp: issuedAt + 60, + iat: issuedAt, + iss: "dify", + sub: "dify-workspace:tenant-1", + }, + secret, + ); + const verifier = createApiAuthVerifier({ + KNOWLEDGE_FS_JWT_SECRET: secret, + NODE_ENV: nodeEnvironment, + }); + + await expect(verifier?.verify(token)).resolves.toEqual({ + callerKind: "interactive", + subject: { + scopes: ["knowledge-spaces:write"], + subjectId: "dify-workspace:tenant-1", + tenantId: "tenant-1", + }, + }); + }, + ); + + it("keeps explicit local auth available alongside Dify JWT auth", async () => { + const verifier = createApiAuthVerifier({ + KNOWLEDGE_DEV_AUTH_TOKEN: "local-secret", + KNOWLEDGE_FS_JWT_SECRET: "test-secret-with-at-least-32-bytes", + NODE_ENV: "development", + }); + + await expect(verifier?.verify("local-secret")).resolves.toEqual({ + scopes: ["knowledge-spaces:*"], + subjectId: "dev-user", + tenantId: "tenant-dev", + }); + }); + it.each(["development", "test"])( "accepts the default local dev token in %s mode", async (nodeEnvironment) => { @@ -56,3 +105,12 @@ describe("createApiAuthVerifier", () => { }, ); }); + +function signJwt(payload: Readonly>, secret: string): string { + const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url"); + const claims = Buffer.from(JSON.stringify(payload)).toString("base64url"); + const input = `${header}.${claims}`; + const signature = createHmac("sha256", secret).update(input).digest("base64url"); + + return `${input}.${signature}`; +} diff --git a/knowledge-fs/apps/api/src/auth-options.ts b/knowledge-fs/apps/api/src/auth-options.ts index 0e11555e1c7..f3b0e9ac6c5 100644 --- a/knowledge-fs/apps/api/src/auth-options.ts +++ b/knowledge-fs/apps/api/src/auth-options.ts @@ -1,22 +1,35 @@ -import { type AuthVerifier, createStaticAuthVerifier } from "@knowledge/api"; +import { type AuthVerifier, createJwtAuthVerifier, createStaticAuthVerifier } from "@knowledge/api"; const DEFAULT_LOCAL_AUTH_TOKEN = "dev-token"; +const DIFY_JWT_AUDIENCE = "knowledge-fs"; +const DIFY_JWT_ISSUER = "dify"; +const DIFY_JWT_MAX_TTL_SECONDS = 60; export interface ApiAuthEnv { readonly KNOWLEDGE_DEV_AUTH_TOKEN?: string | undefined; readonly KNOWLEDGE_DEV_SUBJECT_ID?: string | undefined; readonly KNOWLEDGE_DEV_TENANT_ID?: string | undefined; + readonly KNOWLEDGE_FS_JWT_SECRET?: string | undefined; readonly NODE_ENV?: string | undefined; } export function createApiAuthVerifier(env: ApiAuthEnv = process.env): AuthVerifier | undefined { + const difyJwtSecret = env.KNOWLEDGE_FS_JWT_SECRET?.trim(); const token = getLocalAuthToken(env); + const difyJwtAuth = difyJwtSecret + ? createJwtAuthVerifier({ + audience: DIFY_JWT_AUDIENCE, + issuer: DIFY_JWT_ISSUER, + maxTtlSeconds: DIFY_JWT_MAX_TTL_SECONDS, + secret: difyJwtSecret, + }) + : undefined; if (!token) { - return undefined; + return difyJwtAuth; } - return createStaticAuthVerifier({ + const localAuth = createStaticAuthVerifier({ subject: { scopes: ["knowledge-spaces:*"], subjectId: env.KNOWLEDGE_DEV_SUBJECT_ID?.trim() || "dev-user", @@ -24,6 +37,14 @@ export function createApiAuthVerifier(env: ApiAuthEnv = process.env): AuthVerifi }, token, }); + if (!difyJwtAuth) { + return localAuth; + } + + return { + verify: async (candidate) => + (await difyJwtAuth.verify(candidate)) ?? localAuth.verify(candidate), + }; } function getLocalAuthToken(env: ApiAuthEnv): string | undefined { diff --git a/knowledge-fs/apps/api/src/dify-datasource-invocation-client.test.ts b/knowledge-fs/apps/api/src/dify-datasource-invocation-client.test.ts index ad18f1e250e..307f8c82acc 100644 --- a/knowledge-fs/apps/api/src/dify-datasource-invocation-client.test.ts +++ b/knowledge-fs/apps/api/src/dify-datasource-invocation-client.test.ts @@ -61,6 +61,48 @@ describe("createDifyDatasourceInvocationClient", () => { expect(JSON.stringify(getOnlineDocumentPages.mock.calls)).not.toContain("credentials"); }); + it("maps product crawl options to the Firecrawl datasource parameters", async () => { + const getWebsiteCrawl = vi.fn(() => chunks({ result: { web_info_list: [] } })); + const adapter = createDifyDatasourceInvocationClient({ + client: difyClient({ getWebsiteCrawl }), + }); + const source: Source = { + ...SOURCE, + metadata: { + credentialId: "dify-credential-1", + crawlOptions: { includeSubpages: false, limit: 1 }, + datasource: "crawl", + parameters: { formats: ["markdown"] }, + pluginId: "langgenius/firecrawl_datasource", + provider: "firecrawl", + }, + type: "web", + uri: "https://example.com", + }; + + await collect( + adapter.dispatch({ + operation: "get_website_crawl", + source, + tenantId: "tenant-1", + }), + ); + + expect(getWebsiteCrawl).toHaveBeenCalledWith({ + credentialId: "dify-credential-1", + datasource: "crawl", + datasourceParameters: { + crawl_subpages: false, + formats: ["markdown"], + limit: 1, + url: "https://example.com", + }, + pluginId: "langgenius/firecrawl_datasource", + provider: "firecrawl", + tenantId: "tenant-1", + }); + }); + it("rejects inline credentials in integrated mode", async () => { const adapter = createDifyDatasourceInvocationClient({ client: difyClient() }); const source = { diff --git a/knowledge-fs/apps/api/src/dify-datasource-invocation-client.ts b/knowledge-fs/apps/api/src/dify-datasource-invocation-client.ts index 305bc4084af..2de37e17e41 100644 --- a/knowledge-fs/apps/api/src/dify-datasource-invocation-client.ts +++ b/knowledge-fs/apps/api/src/dify-datasource-invocation-client.ts @@ -38,7 +38,11 @@ export function createDifyDatasourceInvocationClient(input: { case "get_website_crawl": yield* input.client.getWebsiteCrawl({ ...common, - datasourceParameters: withCrawlUrl(config.parameters, invocation.source.uri), + datasourceParameters: withCrawlOptions( + config.parameters, + invocation.source, + invocation.source.uri, + ), }); return; case "get_online_document_pages": @@ -145,6 +149,24 @@ function withCrawlUrl(parameters: Record, uri: string): Record< : { ...parameters, url: uri }; } +function withCrawlOptions( + parameters: Record, + source: Source, + uri: string, +): Record { + const crawlOptions = plainObject(source.metadata.crawlOptions); + const includeSubpages = crawlOptions.includeSubpages; + const limit = crawlOptions.limit; + return withCrawlUrl( + { + ...parameters, + ...(typeof includeSubpages === "boolean" ? { crawl_subpages: includeSubpages } : {}), + ...(Number.isSafeInteger(limit) && Number(limit) > 0 ? { limit } : {}), + }, + uri, + ); +} + function decodeNextPageParameters(token: string): Record { try { const value = JSON.parse(Buffer.from(token, "base64url").toString("utf8")) as unknown; diff --git a/knowledge-fs/apps/api/src/website-crawl-options.test.ts b/knowledge-fs/apps/api/src/website-crawl-options.test.ts index 9924578b2e8..68c4d6d8769 100644 --- a/knowledge-fs/apps/api/src/website-crawl-options.test.ts +++ b/knowledge-fs/apps/api/src/website-crawl-options.test.ts @@ -12,6 +12,7 @@ const WEB_SOURCE: WebsiteCrawlInput["source"] = { id: "00000000-0000-4000-8000-000000000001", knowledgeSpaceId: "10000000-0000-4000-8000-000000000001", metadata: { + crawlOptions: { includeSubpages: false, limit: 1 }, datasource: "crawl", parameters: { limit: 5 }, pluginId: "langgenius/firecrawl_datasource", @@ -62,7 +63,7 @@ describe("createApiWebsiteCrawlConnector", () => { const result = await connector.crawl({ source: WEB_SOURCE, tenantId: "tenant-1" }); expect(result).toEqual({ - completed: 2, + completed: 1, pages: [ { content: "# A", @@ -70,10 +71,9 @@ describe("createApiWebsiteCrawlConnector", () => { sourceUrl: "https://example.com/a", title: "A", }, - { content: "# B", sourceUrl: "https://example.com/b" }, ], status: "completed", - total: 2, + total: 1, }); expect(calls).toHaveLength(1); diff --git a/knowledge-fs/apps/api/src/website-crawl-options.ts b/knowledge-fs/apps/api/src/website-crawl-options.ts index f8524f96274..b1fbe03cf2a 100644 --- a/knowledge-fs/apps/api/src/website-crawl-options.ts +++ b/knowledge-fs/apps/api/src/website-crawl-options.ts @@ -20,6 +20,7 @@ export function createApiWebsiteCrawlConnector(input: { return { crawl: async ({ signal, source, tenantId, userId }): Promise => { const pages = new Map(); + const pageLimit = crawlPageLimit(source.metadata.crawlOptions); let status: string | undefined; let total: number | undefined; let completed: number | undefined; @@ -55,15 +56,25 @@ export function createApiWebsiteCrawlConnector(input: { } return { - pages: Array.from(pages.values()), - ...(completed === undefined ? {} : { completed }), + pages: Array.from(pages.values()).slice(0, pageLimit), + ...(completed === undefined ? {} : { completed: Math.min(completed, pageLimit) }), ...(status === undefined ? {} : { status }), - ...(total === undefined ? {} : { total }), + ...(total === undefined ? {} : { total: Math.min(total, pageLimit) }), }; }, }; } +function crawlPageLimit(value: unknown): number { + if (!value || typeof value !== "object" || Array.isArray(value)) { + return Number.POSITIVE_INFINITY; + } + const limit = (value as Readonly>).limit; + return Number.isSafeInteger(limit) && Number(limit) > 0 + ? Number(limit) + : Number.POSITIVE_INFINITY; +} + export function createApiWebsiteCrawlOptions(input: { readonly client: ApiDatasourceInvocationClient; }): { diff --git a/knowledge-fs/packages/api/src/database-deletion-lifecycle-fence-reader.test.ts b/knowledge-fs/packages/api/src/database-deletion-lifecycle-fence-reader.test.ts index 58f92c07ebc..c77fa6bc371 100644 --- a/knowledge-fs/packages/api/src/database-deletion-lifecycle-fence-reader.test.ts +++ b/knowledge-fs/packages/api/src/database-deletion-lifecycle-fence-reader.test.ts @@ -60,6 +60,10 @@ describe.each(["postgres", "tidb"] as const)( expect(calls[0]?.sql).toContain( dialect === "postgres" ? '"knowledge_space_id" = $2' : "`knowledge_space_id` = ?", ); + if (dialect === "postgres") { + expect(calls[0]?.sql).toContain("$3::uuid IS NOT NULL"); + expect(calls[0]?.sql).toContain("$4::uuid IS NOT NULL"); + } expect(calls[0]?.sql).toContain("'knowledge_space'"); expect(calls[0]?.sql).toContain("'source'"); expect(calls[0]?.sql).toContain("'document_asset'"); diff --git a/knowledge-fs/packages/api/src/database-deletion-lifecycle-fence-reader.ts b/knowledge-fs/packages/api/src/database-deletion-lifecycle-fence-reader.ts index 3cfaa7abffd..19b603a912b 100644 --- a/knowledge-fs/packages/api/src/database-deletion-lifecycle-fence-reader.ts +++ b/knowledge-fs/packages/api/src/database-deletion-lifecycle-fence-reader.ts @@ -44,9 +44,13 @@ export function createDatabaseDeletionLifecycleFenceReader( function tombstoneHierarchySql(database: DatabaseAdapter): string { const q = (identifier: string) => quoteDatabaseIdentifier(database, identifier); const p = (position: number) => databasePlaceholder(database, position); + const idParam = (position: number) => + database.dialect === "postgres" ? `${p(position)}::uuid` : p(position); + const sourceId = idParam(3); + const documentAssetId = idParam(4); const columns = ["id", "tenant_id", "knowledge_space_id", "target_type", "target_id"]; const selected = columns.map(q).join(", "); - return `SELECT ${selected} FROM (SELECT ${selected}, 0 AS ${q("fence_priority")} FROM ${q("deletion_jobs")} WHERE ${q("tenant_id")} = ${p(1)} AND ${q("knowledge_space_id")} = ${p(2)} AND ${q("active_slot")} = 1 UNION ALL SELECT ${selected}, 1 AS ${q("fence_priority")} FROM ${q(tombstoneTable)} WHERE ${q("tenant_id")} = ${p(1)} AND ${q("knowledge_space_id")} = ${p(2)} AND ((${q("target_type")} = 'knowledge_space' AND ${q("target_id")} = ${p(2)}) OR (${q("target_type")} = 'source' AND ((${p(3)} IS NOT NULL AND ${q("target_id")} = ${p(3)}) OR (${p(4)} IS NOT NULL AND ${q("target_id")} IN (SELECT source_document.${q("source_id")} FROM ${q("document_assets")} source_document WHERE source_document.${q("knowledge_space_id")} = ${p(2)} AND source_document.${q("id")} = ${p(4)} AND source_document.${q("source_id")} IS NOT NULL)))) OR (${p(4)} IS NOT NULL AND ${q("target_type")} = 'document_asset' AND ${q("target_id")} = ${p(4)}) OR (${p(4)} IS NOT NULL AND ${q("target_type")} = 'logical_document' AND ${q("target_id")} IN (SELECT logical_revision.${q("document_id")} FROM ${q("document_revisions")} logical_revision WHERE logical_revision.${q("tenant_id")} = ${p(1)} AND logical_revision.${q("knowledge_space_id")} = ${p(2)} AND logical_revision.${q("document_asset_id")} = ${p(4)}))) AS lifecycle_fence ORDER BY ${q("fence_priority")} ASC, CASE ${q("target_type")} WHEN 'knowledge_space' THEN 0 WHEN 'source' THEN 1 ELSE 2 END ASC LIMIT 1;`; + return `SELECT ${selected} FROM (SELECT ${selected}, 0 AS ${q("fence_priority")} FROM ${q("deletion_jobs")} WHERE ${q("tenant_id")} = ${p(1)} AND ${q("knowledge_space_id")} = ${p(2)} AND ${q("active_slot")} = 1 UNION ALL SELECT ${selected}, 1 AS ${q("fence_priority")} FROM ${q(tombstoneTable)} WHERE ${q("tenant_id")} = ${p(1)} AND ${q("knowledge_space_id")} = ${p(2)} AND ((${q("target_type")} = 'knowledge_space' AND ${q("target_id")} = ${p(2)}) OR (${q("target_type")} = 'source' AND ((${sourceId} IS NOT NULL AND ${q("target_id")} = ${sourceId}) OR (${documentAssetId} IS NOT NULL AND ${q("target_id")} IN (SELECT source_document.${q("source_id")} FROM ${q("document_assets")} source_document WHERE source_document.${q("knowledge_space_id")} = ${p(2)} AND source_document.${q("id")} = ${documentAssetId} AND source_document.${q("source_id")} IS NOT NULL)))) OR (${documentAssetId} IS NOT NULL AND ${q("target_type")} = 'document_asset' AND ${q("target_id")} = ${documentAssetId}) OR (${documentAssetId} IS NOT NULL AND ${q("target_type")} = 'logical_document' AND ${q("target_id")} IN (SELECT logical_revision.${q("document_id")} FROM ${q("document_revisions")} logical_revision WHERE logical_revision.${q("tenant_id")} = ${p(1)} AND logical_revision.${q("knowledge_space_id")} = ${p(2)} AND logical_revision.${q("document_asset_id")} = ${documentAssetId})))) AS lifecycle_fence ORDER BY ${q("fence_priority")} ASC, CASE ${q("target_type")} WHEN 'knowledge_space' THEN 0 WHEN 'source' THEN 1 ELSE 2 END ASC LIMIT 1;`; } function mapFence(row: DatabaseRow): ActiveDeletionLifecycleFence { diff --git a/knowledge-fs/packages/api/src/database-durable-deletion-target-capabilities.test.ts b/knowledge-fs/packages/api/src/database-durable-deletion-target-capabilities.test.ts index 6d471475cce..db8c1a8467c 100644 --- a/knowledge-fs/packages/api/src/database-durable-deletion-target-capabilities.test.ts +++ b/knowledge-fs/packages/api/src/database-durable-deletion-target-capabilities.test.ts @@ -279,9 +279,14 @@ describe("database durable deletion target capabilities", () => { signal, }), ).resolves.toEqual({ complete: true, items: [], scanPhase: "document_objects:6" }); - expect( - calls.filter((call) => call.tableName === "document_multimodal_manifests"), - ).toHaveLength(3); + const manifestDatabaseCalls = calls.filter( + (call) => call.tableName === "document_multimodal_manifests", + ); + expect(manifestDatabaseCalls).toHaveLength(3); + expect(manifestDatabaseCalls[0]?.params).toEqual(["tenant-a", spaceId, targetDocumentId]); + expect(manifestDatabaseCalls[0]?.sql).not.toContain( + dialect === "postgres" ? 'manifest."id" >' : "manifest.`id` >", + ); }); it(`inventories and executes space objects, lifecycle secrets, source secrets, and cache items (${dialect})`, async () => { @@ -371,6 +376,18 @@ describe("database durable deletion target capabilities", () => { ], scanPhase: "source_secrets", }); + expect( + calls.find((call) => call.operation === "select" && call.tableName === "sources")?.sql, + ).toContain(dialect === "postgres" ? `"credential_ref" <> ''` : "`credential_ref` <> ''"); + expect( + calls.find( + (call) => + call.operation === "select" && call.tableName === "source_secret_lifecycle_refs", + )?.params, + ).toEqual(["tenant-a", spaceId, null, 2]); + expect( + calls.find((call) => call.operation === "select" && call.tableName === "sources")?.params, + ).toEqual(["tenant-a", spaceId, null, 2]); await capabilities.executeExternalItem({ item: deletionItem("object", { objectKey: firstObjectKey }), @@ -419,6 +436,26 @@ describe("database durable deletion target capabilities", () => { } }); + it(`uses a nullable UUID cursor for the first document inventory page (${dialect})`, async () => { + const calls: DatabaseExecuteInput[] = []; + const capabilities = capabilitiesFor(dialect, async (input) => { + calls.push(input); + return result([]); + }); + + await capabilities.inventory({ + job: job({ targetType: "source" }), + limit: 2, + signal: new AbortController().signal, + }); + + const documentCall = calls.find( + (call) => call.operation === "select" && call.tableName === "document_assets", + ); + expect(documentCall?.params).toEqual([spaceId, null, targetDocumentId]); + expect(documentCall?.sql).toContain("COALESCE"); + }); + it(`publishes a target-free, graph-closed head while preserving unrelated Deep members (${dialect})`, async () => { const calls: DatabaseExecuteInput[] = []; let targetProbeCount = 0; @@ -861,9 +898,11 @@ describe("database durable deletion target capabilities", () => { (call) => call.operation === "select" && call.tableName === "knowledge_fs_sessions", ), ).toBe(true); - expect( - calls.some((call) => call.operation === "select" && call.tableName === "golden_questions"), - ).toBe(true); + const goldenQuestionSelect = calls.find( + (call) => call.operation === "select" && call.tableName === "golden_questions", + ); + expect(goldenQuestionSelect?.params).toEqual([spaceId, 7]); + expect(goldenQuestionSelect?.sql).toContain("1 = 1"); expect( calls.some( (call) => call.operation === "select" && call.tableName === "research_task_jobs", @@ -890,6 +929,45 @@ describe("database durable deletion target capabilities", () => { expect(calls.some((call) => call.tableName === "answer_traces")).toBe(false); }); + it(`preserves the original derived-cleanup error when the transaction is aborted (${dialect})`, async () => { + let fenceChecks = 0; + const execute = async (input: DatabaseExecuteInput): Promise => { + if (input.operation === "select" && input.tableName === "deletion_jobs") { + fenceChecks += 1; + if (fenceChecks > 1) throw new Error("transaction aborted"); + return result([{ id: job().id }]); + } + if (input.operation === "select" && input.tableName === "golden_questions") { + throw new Error("golden question cleanup failed"); + } + return result([]); + }; + + const database = createSchemaDatabaseAdapter({ + executor: execute, + kind: dialect, + transaction: async (callback) => callback({ execute }), + }); + const capabilities = createDatabaseDurableDeletionTargetCapabilities({ + cache: createMemoryCacheAdapter({ maxEntries: 10 }), + database, + objectStorage: createMemoryObjectStorageAdapter({ + kind: "memory", + maxObjectBytes: 1_024, + }), + secretStore: { delete: vi.fn(async () => undefined) }, + }); + + await expect( + capabilities.deleteDerivedDataPage({ + job: job({ deleteMode: "keep", targetType: "source" }), + limit: 7, + signal: new AbortController().signal, + }), + ).rejects.toThrow("golden question cleanup failed"); + expect(fenceChecks).toBe(1); + }); + it(`source keep still drains live whole-space Research writers (${dialect})`, async () => { const calls: DatabaseExecuteInput[] = []; const execute = async (input: DatabaseExecuteInput): Promise => { @@ -1004,6 +1082,14 @@ describe("database durable deletion target capabilities", () => { expect(childUpdates[0]?.sql).toContain("deleting_at"); expect(childUpdates[1]?.sql).toContain("deletion_job_id"); expect(childUpdates[1]?.sql).toContain("IS NULL"); + const logicalDocumentUpdate = calls.find( + (call) => call.operation === "update" && call.tableName === "logical_documents", + ); + expect(logicalDocumentUpdate?.sql).toContain( + dialect === "postgres" + ? `"provider_item_digest" = NULL` + : "`provider_item_digest` = NULL", + ); const childResidue = calls.find( (call) => call.operation === "select" && call.tableName === "document_assets", ); @@ -1116,6 +1202,13 @@ describe("database durable deletion target capabilities", () => { expect(select?.sql).toContain("knowledge_space_staged_commits"); expect(select?.sql).not.toContain('target_lease."document_asset_id"'); expect(select?.sql).not.toContain("target_lease.`document_asset_id`"); + if (dialect === "postgres") { + expect(select?.sql).toContain('target_lease."target_id" = CAST(CAST($2 AS UUID) AS TEXT)'); + expect(select?.sql).toContain('target_lease."target_id" = CAST(CAST($3 AS UUID) AS TEXT)'); + expect(select?.sql).toContain( + "semantic_document_ref.document_asset_id = CAST(CAST($3 AS UUID) AS TEXT)", + ); + } expect( calls.find( (call) => call.operation === "delete" && call.tableName === "knowledge_fs_leases", @@ -1184,6 +1277,7 @@ describe("database durable deletion target capabilities", () => { ["document_multimodal_manifests", [{ id: "018f0d60-7a49-7cc2-9c1b-5b36f18f2d25" }]], ["knowledge_paths", [{ id: "018f0d60-7a49-7cc2-9c1b-5b36f18f2d26" }]], ["knowledge_space_staged_commits", [{ id: "018f0d60-7a49-7cc2-9c1b-5b36f18f2d27" }]], + ["parse_artifacts", [{ id: "018f0d60-7a49-7cc2-9c1b-5b36f18f2d28" }]], ]); const calls: DatabaseExecuteInput[] = []; const execute = async (input: DatabaseExecuteInput): Promise => { @@ -1198,7 +1292,7 @@ describe("database durable deletion target capabilities", () => { }; const capabilities = capabilitiesFor(dialect, execute); - for (let page = 0; page < 7; page += 1) { + for (let page = 0; page < 8; page += 1) { await expect( capabilities.deleteDerivedDataPage({ job: job(), @@ -1220,6 +1314,7 @@ describe("database durable deletion target capabilities", () => { "document_outlines", "document_multimodal_manifests", "knowledge_space_staged_commits", + "parse_artifacts", ]); const pathSelect = calls.find( (call) => call.operation === "select" && call.tableName === "knowledge_paths", @@ -1229,6 +1324,45 @@ describe("database durable deletion target capabilities", () => { expect(pathSelect?.sql).toContain("documentAssetIds"); expect(pathSelect?.sql).toContain("sourceSummaryNodeIds"); expect(pathSelect?.sql).toContain("communityId"); + const parseArtifactSelect = calls.find( + (call) => call.operation === "select" && call.tableName === "parse_artifacts", + ); + expect(parseArtifactSelect?.sql).toContain("document_assets"); + expect(parseArtifactSelect?.sql).toContain("knowledge_space_id"); + }); + + it(`scopes the final parse-artifact residue probe to the target knowledge space (${dialect})`, async () => { + const calls: DatabaseExecuteInput[] = []; + const execute = async (input: DatabaseExecuteInput): Promise => { + calls.push(input); + if (input.operation === "select" && input.tableName === "deletion_jobs") { + return result([{ id: job().id }]); + } + if (input.operation === "select" && input.tableName === "knowledge_space_manifests") { + return result([{ object_key_prefix: `tenant-a/spaces/${spaceId}` }]); + } + return result([]); + }; + const targetJob = job(); + + await expect( + capabilitiesFor(dialect, execute).deletePrimaryData({ + job: targetJob, + leaseFence: { + deletionJobId: targetJob.id, + expectedRowVersion: targetJob.rowVersion, + leaseToken: targetJob.leaseToken as string, + }, + signal: new AbortController().signal, + transaction: { execute }, + }), + ).resolves.toEqual({ clean: true }); + + const parseArtifactProbe = calls.find( + (call) => call.operation === "select" && call.tableName === "parse_artifacts", + ); + expect(parseArtifactProbe?.sql).toContain("document_assets"); + expect(parseArtifactProbe?.sql).toContain("knowledge_space_id"); }); it(`fails the space primary proof when any cascaded derived row survives (${dialect})`, async () => { diff --git a/knowledge-fs/packages/api/src/database-durable-deletion-target-capabilities.ts b/knowledge-fs/packages/api/src/database-durable-deletion-target-capabilities.ts index 6dcac9aa2e1..8aa1a8ab9f5 100644 --- a/knowledge-fs/packages/api/src/database-durable-deletion-target-capabilities.ts +++ b/knowledge-fs/packages/api/src/database-durable-deletion-target-capabilities.ts @@ -369,6 +369,7 @@ export function createDatabaseDurableDeletionTargetCapabilities({ }); throwIfAborted(signal); const preservesDocuments = job.targetType === "source" && job.deleteMode === "keep"; + let operationFailed = false; try { // Command logs, KnowledgeFS session metadata, Golden Question metadata, and Research inputs // are opaque JSON. They cannot be attributed safely to one document/source, so every target @@ -544,10 +545,13 @@ export function createDatabaseDurableDeletionTargetCapabilities({ } } return { complete: true, deleted: 0 }; + } catch (error) { + operationFailed = true; + throw error; } finally { // A cache/object adapter call can outlive the original lease. Recheck immediately before // commit so every DB page rolls back when the worker fence expired mid-operation. - await assertJobFence(database, transaction, job); + if (!operationFailed) await assertJobFence(database, transaction, job); } }); }, @@ -1980,7 +1984,7 @@ async function cleanupLogicalDocumentsForPrimaryDeletion( maxRows: 0, operation: "update", params: [job.tenantId, job.knowledgeSpaceId, job.targetId, job.updatedAt], - sql: `UPDATE ${q("logical_documents")} SET ${q("source_id")} = NULL, ${q("provider_item_id")} = NULL, ${q("system_metadata")} = ${scrubSourceIdentityMetadataSql(database, q("system_metadata"), false)}, ${q("row_version")} = ${q("row_version")} + 1, ${q("updated_at")} = ${p(4)} WHERE ${q("tenant_id")} = ${p(1)} AND ${q("knowledge_space_id")} = ${p(2)} AND ${q("source_id")} = ${p(3)};`, + sql: `UPDATE ${q("logical_documents")} SET ${q("source_id")} = NULL, ${q("provider_item_id")} = NULL, ${q("provider_item_digest")} = NULL, ${q("system_metadata")} = ${scrubSourceIdentityMetadataSql(database, q("system_metadata"), false)}, ${q("row_version")} = ${q("row_version")} + 1, ${q("updated_at")} = ${p(4)} WHERE ${q("tenant_id")} = ${p(1)} AND ${q("knowledge_space_id")} = ${p(2)} AND ${q("source_id")} = ${p(3)};`, tableName: "logical_documents", }); return; @@ -2763,7 +2767,8 @@ async function nextTargetDocumentId( if (job.targetType === "document_asset") return cursor ? undefined : job.targetId; const q = (value: string) => quoteDatabaseIdentifier(database, value); const p = (position: number) => databasePlaceholder(database, position); - const params: DatabaseQueryValue[] = [job.knowledgeSpaceId, cursor ?? ""]; + const params: DatabaseQueryValue[] = [job.knowledgeSpaceId, cursor ?? null]; + const after = nullableUuidCursorExpression(database, p(2)); let target = ""; if (job.targetType === "source") { params.push(job.targetId); @@ -2776,7 +2781,7 @@ async function nextTargetDocumentId( maxRows: 1, operation: "select", params, - sql: `SELECT ${q("id")} FROM ${q("document_assets")} WHERE ${q("knowledge_space_id")} = ${p(1)} AND ${q("id")} > ${p(2)}${target} ORDER BY ${q("id")} ASC LIMIT 1;`, + sql: `SELECT ${q("id")} FROM ${q("document_assets")} WHERE ${q("knowledge_space_id")} = ${p(1)} AND ${q("id")} > ${after}${target} ORDER BY ${q("id")} ASC LIMIT 1;`, tableName: "document_assets", }); return result.rows[0] ? stringColumn(result.rows[0], "id") : undefined; @@ -2801,12 +2806,12 @@ async function documentManifestObjectKeyPage( const p = (position: number) => databasePlaceholder(database, position); const activeId = state.manifestActiveId; const params: DatabaseQueryValue[] = [job.tenantId, job.knowledgeSpaceId, documentId]; - let cursorPredicate: string; + let cursorPredicate = ""; if (activeId) { params.push(activeId); cursorPredicate = ` AND manifest.${q("id")} = ${p(4)}`; - } else { - params.push(state.manifestAfter ?? ""); + } else if (state.manifestAfter) { + params.push(state.manifestAfter); cursorPredicate = ` AND manifest.${q("id")} > ${p(4)}`; } const result = await database.execute({ @@ -2912,6 +2917,11 @@ interface SecretInventoryRef { readonly rowId: string; } +function nullableUuidCursorExpression(database: DatabaseAdapter, placeholder: string): string { + const value = `COALESCE(${placeholder}, '00000000-0000-0000-0000-000000000000')`; + return database.dialect === "postgres" ? `CAST(${value} AS UUID)` : value; +} + async function lifecycleSecretRefs( database: DatabaseAdapter, job: DurableDeletionTargetOperationInput["job"], @@ -2921,7 +2931,8 @@ async function lifecycleSecretRefs( if (job.targetType === "document_asset" || job.targetType === "logical_document") return []; const q = (value: string) => quoteDatabaseIdentifier(database, value); const p = (position: number) => databasePlaceholder(database, position); - const params: DatabaseQueryValue[] = [job.tenantId, job.knowledgeSpaceId, cursor ?? "", limit]; + const params: DatabaseQueryValue[] = [job.tenantId, job.knowledgeSpaceId, cursor ?? null, limit]; + const after = nullableUuidCursorExpression(database, p(3)); let target = ""; if (job.targetType === "source") { params.push(job.targetId); @@ -2931,7 +2942,7 @@ async function lifecycleSecretRefs( maxRows: limit, operation: "select", params, - sql: `SELECT ${q("id")}, ${q("source_id")}, ${q("credential_ref")} FROM ${q("source_secret_lifecycle_refs")} WHERE ${q("tenant_id")} = ${p(1)} AND ${q("knowledge_space_id")} = ${p(2)} AND ${q("id")} > ${p(3)} AND ${q("state")} <> 'deleted'${target} ORDER BY ${q("id")} ASC LIMIT ${p(4)};`, + sql: `SELECT ${q("id")}, ${q("source_id")}, ${q("credential_ref")} FROM ${q("source_secret_lifecycle_refs")} WHERE ${q("tenant_id")} = ${p(1)} AND ${q("knowledge_space_id")} = ${p(2)} AND ${q("id")} > ${after} AND ${q("state")} <> 'deleted'${target} ORDER BY ${q("id")} ASC LIMIT ${p(4)};`, tableName: "source_secret_lifecycle_refs", }); return result.rows.map((row) => ({ @@ -2965,7 +2976,8 @@ async function sourceSecretRefs( if (job.targetType === "document_asset" || job.targetType === "logical_document") return []; const q = (value: string) => quoteDatabaseIdentifier(database, value); const p = (position: number) => databasePlaceholder(database, position); - const params: DatabaseQueryValue[] = [job.tenantId, job.knowledgeSpaceId, cursor ?? "", limit]; + const params: DatabaseQueryValue[] = [job.tenantId, job.knowledgeSpaceId, cursor ?? null, limit]; + const after = nullableUuidCursorExpression(database, p(3)); let target = ""; if (job.targetType === "source") { params.push(job.targetId); @@ -2975,7 +2987,7 @@ async function sourceSecretRefs( maxRows: limit, operation: "select", params, - sql: `SELECT s.${q("id")}, s.${q("credential_ref")} FROM ${q("sources")} s WHERE s.${q("knowledge_space_id")} = ${p(2)} AND s.${q("id")} > ${p(3)} AND s.${q("credential_ref")} IS NOT NULL${target} AND EXISTS (SELECT 1 FROM ${q("knowledge_spaces")} ks WHERE ks.${q("tenant_id")} = ${p(1)} AND ks.${q("id")} = ${p(2)}) AND NOT EXISTS (SELECT 1 FROM ${q("source_secret_lifecycle_refs")} lifecycle WHERE lifecycle.${q("tenant_id")} = ${p(1)} AND lifecycle.${q("knowledge_space_id")} = ${p(2)} AND lifecycle.${q("source_id")} = s.${q("id")} AND lifecycle.${q("credential_ref")} = s.${q("credential_ref")}) ORDER BY s.${q("id")} ASC LIMIT ${p(4)};`, + sql: `SELECT s.${q("id")}, s.${q("credential_ref")} FROM ${q("sources")} s WHERE s.${q("knowledge_space_id")} = ${p(2)} AND s.${q("id")} > ${after} AND s.${q("credential_ref")} IS NOT NULL AND s.${q("credential_ref")} <> ''${target} AND EXISTS (SELECT 1 FROM ${q("knowledge_spaces")} ks WHERE ks.${q("tenant_id")} = ${p(1)} AND ks.${q("id")} = ${p(2)}) AND NOT EXISTS (SELECT 1 FROM ${q("source_secret_lifecycle_refs")} lifecycle WHERE lifecycle.${q("tenant_id")} = ${p(1)} AND lifecycle.${q("knowledge_space_id")} = ${p(2)} AND lifecycle.${q("source_id")} = s.${q("id")} AND lifecycle.${q("credential_ref")} = s.${q("credential_ref")}) ORDER BY s.${q("id")} ASC LIMIT ${p(4)};`, tableName: "sources", }); return result.rows.map((row) => ({ @@ -3128,7 +3140,7 @@ async function deleteGoldenQuestionPage( const q = (value: string) => quoteDatabaseIdentifier(database, value); const p = (position: number) => databasePlaceholder(database, position); return database.transaction(async (transaction) => { - const params = targetDocumentQueryParams(job); + const params = targetGoldenQuestionQueryParams(job); params.push(limit); const alias = "target_golden_question"; const rows = await transaction.execute({ @@ -3257,6 +3269,15 @@ function goldenMissingEvidencePredicateSql( return `EXISTS (SELECT 1 FROM JSON_TABLE(${metadata}, '$.evidenceContext.missingEvidence[*]' COLUMNS (evidence_id VARCHAR(255) PATH '$.expectedEvidenceId')) AS golden_missing WHERE ${target})`; } +function targetGoldenQuestionQueryParams( + job: DurableDeletionTargetOperationInput["job"], +): DatabaseQueryValue[] { + return job.targetType === "knowledge_space" || + (job.targetType === "source" && job.deleteMode === "keep") + ? [job.knowledgeSpaceId] + : targetDocumentQueryParams(job); +} + function targetDocumentQueryParams( job: DurableDeletionTargetOperationInput["job"], ): DatabaseQueryValue[] { @@ -3354,7 +3375,12 @@ function targetDocumentMembershipAtSql( const q = (value: string) => quoteDatabaseIdentifier(database, value); const p = (position: number) => databasePlaceholder(database, position); if (job.targetType === "document_asset") { - return `${documentIdExpression} = ${p(targetParamPosition)}`; + const targetParameter = textComparison + ? database.dialect === "postgres" + ? `CAST(CAST(${p(targetParamPosition)} AS UUID) AS TEXT)` + : `CAST(${p(targetParamPosition)} AS CHAR(36))` + : p(targetParamPosition); + return `${documentIdExpression} = ${targetParameter}`; } if (job.targetType === "logical_document") { const selectedRevisionAsset = textComparison @@ -3589,7 +3615,7 @@ async function deleteDocumentDerivedResiduePage( maxRows: limit, operation: "select", params, - sql: `SELECT artifact.${q("id")} FROM ${q("parse_artifacts")} AS artifact WHERE artifact.${q("document_asset_id")} ${documentPredicate} ORDER BY artifact.${q("id")} ASC LIMIT ${p(2)};`, + sql: `SELECT artifact.${q("id")} FROM ${q("parse_artifacts")} AS artifact WHERE artifact.${q("document_asset_id")} ${documentPredicate} AND EXISTS (SELECT 1 FROM ${q("document_assets")} AS target_document WHERE target_document.${q("id")} = artifact.${q("document_asset_id")} AND target_document.${q("knowledge_space_id")} = ${p(1)}) ORDER BY artifact.${q("id")} ASC LIMIT ${p(2)};`, tableName: "parse_artifacts", }); const parseArtifactIds = parseArtifacts.rows.map((row) => stringColumn(row, "id")); @@ -3797,7 +3823,7 @@ async function hasTargetGoldenQuestionResidue( const result = await executor.execute({ maxRows: 1, operation: "select", - params: targetDocumentQueryParams(job), + params: targetGoldenQuestionQueryParams(job), sql: `SELECT ${alias}.${q("id")} FROM ${q("golden_questions")} AS ${alias} WHERE ${alias}.${q("knowledge_space_id")} = ${p(1)} AND ${targetGoldenQuestionPredicateSql(database, job, alias)} LIMIT 1;`, tableName: "golden_questions", }); @@ -4145,7 +4171,7 @@ async function hasTargetDocumentForeignKeyResidue( table: "artifact_segments", }, { - sql: `SELECT artifact.${q("id")} FROM ${q("parse_artifacts")} AS artifact WHERE artifact.${q("document_asset_id")} ${documentPredicate} LIMIT 1;`, + sql: `SELECT artifact.${q("id")} FROM ${q("parse_artifacts")} AS artifact WHERE artifact.${q("document_asset_id")} ${documentPredicate} AND EXISTS (SELECT 1 FROM ${q("document_assets")} AS target_document WHERE target_document.${q("id")} = artifact.${q("document_asset_id")} AND target_document.${q("knowledge_space_id")} = ${p(1)}) LIMIT 1;`, table: "parse_artifacts", }, ] as const; @@ -4373,6 +4399,10 @@ function targetKnowledgeFsLeasePredicateSql( const q = (value: string) => quoteDatabaseIdentifier(database, value); const p = (position: number) => databasePlaceholder(database, position); const field = (column: string) => `${leaseAlias}.${q(column)}`; + const textParam = (position: number) => + database.dialect === "postgres" + ? `CAST(CAST(${p(position)} AS UUID) AS TEXT)` + : `CAST(${p(position)} AS CHAR(36))`; const scope = `${field("tenant_id")} = ${p(1)} AND ${field("knowledge_space_id")} = ${p(2)}`; if (job.targetType === "knowledge_space") return scope; @@ -4406,7 +4436,7 @@ function targetKnowledgeFsLeasePredicateSql( const pathTarget = `${field("target_type")} = 'knowledge-path' AND EXISTS (SELECT 1 FROM ${q("knowledge_paths")} AS target_path WHERE target_path.${q("knowledge_space_id")} = ${p(2)} AND (${field("target_id")} = ${castId("target_path")} OR ${field("target_id")} = target_path.${q("target_id")} OR ${field("virtual_path")} = target_path.${q("virtual_path")}) AND ${targetSemanticPathPredicateSql(database, uuidDocumentPredicate, textDocumentPredicate, 2, "target_path")})`; const stagedCommitTarget = `${field("target_type")} = 'staged-commit' AND EXISTS (SELECT 1 FROM ${q("knowledge_space_staged_commits")} AS target_commit WHERE target_commit.${q("tenant_id")} = ${p(1)} AND target_commit.${q("knowledge_space_id")} = ${p(2)} AND ${uuidDocumentMembership(`target_commit.${q("document_asset_id")}`)} AND (${field("target_id")} = ${castId("target_commit")} OR ${field("target_id")} = target_commit.${q("raw_object_key")} OR ${field("target_id")} = target_commit.${q("published_object_key")}))`; - return `${scope} AND ((${field("target_type")} = 'knowledge-space' AND ${field("target_id")} = ${p(2)}) OR (${field("target_type")} = 'document-asset' AND ${textDocumentMembership(field("target_id"))}) OR ${documentVirtualPath} OR ${textDocumentMembership(metadataDocumentId)} OR (${parseArtifactTarget}) OR (${projectionTarget}) OR (${pathTarget}) OR (${stagedCommitTarget}))`; + return `${scope} AND ((${field("target_type")} = 'knowledge-space' AND ${field("target_id")} = ${textParam(2)}) OR (${field("target_type")} = 'document-asset' AND ${textDocumentMembership(field("target_id"))}) OR ${documentVirtualPath} OR ${textDocumentMembership(metadataDocumentId)} OR (${parseArtifactTarget}) OR (${projectionTarget}) OR (${pathTarget}) OR (${stagedCommitTarget}))`; } async function hasActiveMutationLease( diff --git a/knowledge-fs/packages/api/src/database-row-utils.test.ts b/knowledge-fs/packages/api/src/database-row-utils.test.ts index 15ca79d053e..bb7194b6ac6 100644 --- a/knowledge-fs/packages/api/src/database-row-utils.test.ts +++ b/knowledge-fs/packages/api/src/database-row-utils.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; import { + nonnegativeSafeIntegerColumn, numberColumn, optionalNumberColumn, optionalStringColumn, @@ -17,6 +18,8 @@ describe("database-row-utils", () => { it("reads required and optional number columns", () => { expect(numberColumn({ count: 3 }, "count")).toBe(3); + expect(nonnegativeSafeIntegerColumn({ count: "3" }, "count")).toBe(3); + expect(nonnegativeSafeIntegerColumn({ count: 3 }, "count")).toBe(3); expect(optionalNumberColumn({ count: null }, "count")).toBeUndefined(); expect(optionalNumberColumn({ count: undefined }, "count")).toBeUndefined(); expect(optionalNumberColumn({ count: 3 }, "count")).toBe(3); @@ -35,5 +38,11 @@ describe("database-row-utils", () => { expect(() => optionalNumberColumn({ count: "3" }, "count")).toThrow( "Database row column count must be a number", ); + expect(() => nonnegativeSafeIntegerColumn({ count: "-1" }, "count")).toThrow( + "Database row column count must be a nonnegative safe integer", + ); + expect(() => nonnegativeSafeIntegerColumn({ count: "9007199254740992" }, "count")).toThrow( + "Database row column count must be a nonnegative safe integer", + ); }); }); diff --git a/knowledge-fs/packages/api/src/database-row-utils.ts b/knowledge-fs/packages/api/src/database-row-utils.ts index bb958512503..7e6e977103b 100644 --- a/knowledge-fs/packages/api/src/database-row-utils.ts +++ b/knowledge-fs/packages/api/src/database-row-utils.ts @@ -34,6 +34,17 @@ export function numberColumn(row: DatabaseRow, column: string): number { return value; } +export function nonnegativeSafeIntegerColumn(row: DatabaseRow, column: string): number { + const raw = row[column]; + const value = typeof raw === "string" && /^\d+$/u.test(raw) ? Number(raw) : raw; + + if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) { + throw new Error(`Database row column ${column} must be a nonnegative safe integer`); + } + + return value; +} + export function optionalNumberColumn(row: DatabaseRow, column: string): number | undefined { const value = row[column]; diff --git a/knowledge-fs/packages/api/src/dify-capability-v2.test.ts b/knowledge-fs/packages/api/src/dify-capability-v2.test.ts index d4eba35836c..7ad1e4f3b19 100644 --- a/knowledge-fs/packages/api/src/dify-capability-v2.test.ts +++ b/knowledge-fs/packages/api/src/dify-capability-v2.test.ts @@ -266,6 +266,12 @@ describe("Dify Capability v2 request guard", () => { "sources.crawl", "source", ], + createSourceSyncWorkflow: [ + "POST", + "/knowledge-spaces/{id}/sources/{sourceId}/sync", + "source_workflows.sync.create", + "source", + ], getAnswerTrace: ["GET", "/queries/{traceId}", "queries.read", "query"], getBulkOperation: ["GET", "/bulk-jobs/{id}", "bulk_jobs.read", "job"], getDocument: [ diff --git a/knowledge-fs/packages/api/src/dify-capability-v2.ts b/knowledge-fs/packages/api/src/dify-capability-v2.ts index 0a87dd40fad..b86dcc7d36a 100644 --- a/knowledge-fs/packages/api/src/dify-capability-v2.ts +++ b/knowledge-fs/packages/api/src/dify-capability-v2.ts @@ -521,6 +521,25 @@ export const DIFY_CAPABILITY_V2_OPERATIONS: readonly DifyCapabilityV2Operation[] resource: { pathParameter: "id" }, resourceType: "knowledge_space", }, + { + action: "logical_documents.list", + allowedCallerKinds: STANDARD_CALLERS, + method: "GET", + operationId: "listLogicalDocuments", + pathTemplate: "/knowledge-spaces/{id}/logical-documents", + resource: { pathParameter: "id" }, + resourceType: "knowledge_space", + }, + { + action: "logical_documents.read", + allowedCallerKinds: STANDARD_CALLERS, + method: "GET", + operationId: "getLogicalDocument", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/logical-documents/{documentId}", + resource: { pathParameter: "documentId" }, + resourceType: "document", + }, { action: "documents.create", allowedCallerKinds: STANDARD_CALLERS, @@ -746,6 +765,132 @@ export const DIFY_CAPABILITY_V2_OPERATIONS: readonly DifyCapabilityV2Operation[] resource: { pathParameter: "sourceId" }, resourceType: "source", }, + { + action: "source_workflows.sync.create", + allowedCallerKinds: STANDARD_CALLERS, + method: "POST", + operationId: "createSourceSyncWorkflow", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/sources/{sourceId}/sync", + resource: { pathParameter: "sourceId" }, + resourceType: "source", + }, + { + action: "source_providers.list", + allowedCallerKinds: STANDARD_CALLERS, + method: "GET", + operationId: "listSourceProviders", + pathTemplate: "/source-providers", + resource: { namespace: true }, + resourceType: "namespace", + }, + { + action: "source_connections.create", + allowedCallerKinds: STANDARD_CALLERS, + method: "POST", + operationId: "createSourceConnection", + pathTemplate: "/knowledge-spaces/{id}/source-connections", + resource: { pathParameter: "id" }, + resourceType: "knowledge_space", + }, + { + action: "source_connections.list", + allowedCallerKinds: STANDARD_CALLERS, + method: "GET", + operationId: "listSourceConnections", + pathTemplate: "/knowledge-spaces/{id}/source-connections", + resource: { pathParameter: "id" }, + resourceType: "knowledge_space", + }, + { + action: "source_connections.refresh", + allowedCallerKinds: STANDARD_CALLERS, + method: "POST", + operationId: "refreshSourceConnection", + pathTemplate: "/knowledge-spaces/{id}/source-connections/{connectionId}/refresh", + resource: { pathParameter: "id" }, + resourceType: "knowledge_space", + }, + { + action: "source_workflows.preview.create", + allowedCallerKinds: STANDARD_CALLERS, + method: "POST", + operationId: "createSourceCrawlPreviewWorkflow", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/sources/{sourceId}/crawl-preview", + resource: { pathParameter: "sourceId" }, + resourceType: "source", + }, + { + action: "source_sync_policies.read", + allowedCallerKinds: STANDARD_CALLERS, + method: "GET", + operationId: "getSourceSyncPolicy", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/sources/{sourceId}/sync-policy", + resource: { pathParameter: "sourceId" }, + resourceType: "source", + }, + { + action: "source_sync_policies.update", + allowedCallerKinds: STANDARD_CALLERS, + method: "PUT", + operationId: "putSourceSyncPolicy", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/sources/{sourceId}/sync-policy", + resource: { pathParameter: "sourceId" }, + resourceType: "source", + }, + { + action: "source_workflows.read", + allowedCallerKinds: STANDARD_CALLERS, + method: "GET", + operationId: "getSourceWorkflow", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/source-workflows/{runId}", + resource: { pathParameter: "runId" }, + resourceType: "job", + }, + { + action: "source_workflows.cancel", + allowedCallerKinds: STANDARD_CALLERS, + method: "POST", + operationId: "cancelSourceWorkflow", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/source-workflows/{runId}/cancel", + resource: { pathParameter: "runId" }, + resourceType: "job", + }, + { + action: "source_workflows.retry", + allowedCallerKinds: STANDARD_CALLERS, + method: "POST", + operationId: "retrySourceWorkflow", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/source-workflows/{runId}/retry", + resource: { pathParameter: "runId" }, + resourceType: "job", + }, + { + action: "source_workflows.pages.list", + allowedCallerKinds: STANDARD_CALLERS, + method: "GET", + operationId: "listCrawlPreviewPages", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/source-workflows/{runId}/pages", + resource: { pathParameter: "runId" }, + resourceType: "job", + }, + { + action: "source_workflows.selection.create", + allowedCallerKinds: STANDARD_CALLERS, + method: "POST", + operationId: "selectCrawlPreviewPages", + parentResource: { pathParameter: "id" }, + pathTemplate: "/knowledge-spaces/{id}/source-workflows/{runId}/selection", + resource: { pathParameter: "runId" }, + resourceType: "job", + }, { action: "sources.crawl", allowedCallerKinds: STANDARD_CALLERS, diff --git a/knowledge-fs/packages/api/src/document-compilation-attempt-repository.test.ts b/knowledge-fs/packages/api/src/document-compilation-attempt-repository.test.ts index 2f37aa1bffc..326e758a843 100644 --- a/knowledge-fs/packages/api/src/document-compilation-attempt-repository.test.ts +++ b/knowledge-fs/packages/api/src/document-compilation-attempt-repository.test.ts @@ -1311,8 +1311,10 @@ describe("database document compilation attempt repository", () => { knowledgeSpaceId, candidatePublicationId, candidateFingerprint, - "candidate", + 2, ]); + expect(fake.calls[1]?.sql).toContain("'published'"); + expect(fake.calls[1]?.sql).toContain("projection_set_publication_heads"); expect(fake.calls[1]?.sql).toContain("FOR UPDATE"); }); diff --git a/knowledge-fs/packages/api/src/document-compilation-attempt-repository.ts b/knowledge-fs/packages/api/src/document-compilation-attempt-repository.ts index 9caaa77c704..f0c6c851ac6 100644 --- a/knowledge-fs/packages/api/src/document-compilation-attempt-repository.ts +++ b/knowledge-fs/packages/api/src/document-compilation-attempt-repository.ts @@ -3043,7 +3043,7 @@ function productIntentRestoreConflict(): DocumentCompilationAttemptTransitionErr async function requireDatabaseCandidateBinding( database: DatabaseAdapter, transaction: DatabaseExecutor, - attempt: Pick, + attempt: Pick, candidate: { readonly candidateFingerprint: string; readonly candidatePublicationId: string }, ): Promise { const result = await transaction.execute({ @@ -3054,7 +3054,7 @@ async function requireDatabaseCandidateBinding( uuid(attempt.knowledgeSpaceId, "knowledgeSpaceId"), uuid(candidate.candidatePublicationId, "candidatePublicationId"), ProjectionSetFingerprintSchema.parse(candidate.candidateFingerprint), - "candidate", + nonnegativeInteger(attempt.baseHeadRevision, "baseHeadRevision"), ], sql: `SELECT ${quoteDatabaseIdentifier(database, "id")} FROM ${quoteDatabaseIdentifier( database, @@ -3071,10 +3071,34 @@ async function requireDatabaseCandidateBinding( )} AND ${quoteDatabaseIdentifier(database, "fingerprint")} = ${databasePlaceholder( database, 4, - )} AND ${quoteDatabaseIdentifier(database, "status")} = ${databasePlaceholder( + )} AND (${quoteDatabaseIdentifier(database, "status")} = 'candidate' OR (${quoteDatabaseIdentifier( database, - 5, - )} LIMIT 1 FOR UPDATE;`, + "status", + )} = 'published' AND EXISTS (SELECT 1 FROM ${quoteDatabaseIdentifier( + database, + "projection_set_publication_heads", + )} AS publication_head WHERE publication_head.${quoteDatabaseIdentifier( + database, + "tenant_id", + )} = ${quoteDatabaseIdentifier(database, publicationTableName)}.${quoteDatabaseIdentifier( + database, + "tenant_id", + )} AND publication_head.${quoteDatabaseIdentifier( + database, + "knowledge_space_id", + )} = ${quoteDatabaseIdentifier(database, publicationTableName)}.${quoteDatabaseIdentifier( + database, + "knowledge_space_id", + )} AND publication_head.${quoteDatabaseIdentifier( + database, + "publication_id", + )} = ${quoteDatabaseIdentifier(database, publicationTableName)}.${quoteDatabaseIdentifier( + database, + "id", + )} AND publication_head.${quoteDatabaseIdentifier( + database, + "head_revision", + )} = ${databasePlaceholder(database, 5)}))) LIMIT 1 FOR UPDATE;`, tableName: publicationTableName, }); if (!result.rows[0]) { diff --git a/knowledge-fs/packages/api/src/document-compilation-publication-coordinator.test.ts b/knowledge-fs/packages/api/src/document-compilation-publication-coordinator.test.ts index 6f82d6f3160..5ea5647faf4 100644 --- a/knowledge-fs/packages/api/src/document-compilation-publication-coordinator.test.ts +++ b/knowledge-fs/packages/api/src/document-compilation-publication-coordinator.test.ts @@ -288,6 +288,90 @@ describe("document compilation publication coordinator", () => { expect(compose).not.toHaveBeenCalled(); }); + it("completes a rebuild as a no-op when it resolves to the current published snapshot", async () => { + const publications = createInMemoryProjectionSetPublicationRepository({ maxPublications: 10 }); + const execution = fakeExecution(attempt()); + const members = createInMemoryProjectionSetPublicationMemberRepository({ + attempts: { + get: async (id) => (id === execution.context.attempt.id ? execution.context.attempt : null), + }, + maxListLimit: 10, + maxMembers: 10, + publications, + }); + const compose = vi.spyOn(members, "composeDocumentCandidate"); + const material = fingerprintMaterial(); + const fingerprint = await buildProjectionSetFingerprint(material); + await publications.createCandidate({ + createdAt: now, + fingerprint, + id: conflictingPublicationId, + knowledgeSpaceId, + metadata: { + [DocumentCompilationCandidateMetadataKey]: { + attemptId: conflictingPublicationId, + }, + }, + projectionVersion: 3, + tenantId, + }); + await publications.publish({ + expectedHeadRevision: 0, + fingerprint, + knowledgeSpaceId, + tenantId, + updatedAt: now, + }); + const coordinator = createDocumentCompilationPublicationCoordinator({ + maxComponents: 100, + members, + publications, + validator: allowingValidator(), + }); + + await expect( + coordinator.composeCandidate({ + candidateId: candidatePublicationId, + componentReceipt: replacementReceipt(), + createdAt: now, + execution: execution.context, + fingerprintMaterial: material, + projectionVersion: 3, + }), + ).resolves.toMatchObject({ + attempt: { + candidateFingerprint: fingerprint, + candidatePublicationId: conflictingPublicationId, + checkpoint: "projection_built", + }, + candidate: { id: conflictingPublicationId, status: "published" }, + inheritedMemberCount: 0, + replacedMemberCount: 0, + }); + expect(compose).not.toHaveBeenCalled(); + + await expect( + coordinator.evaluateAndPublishCandidate({ + evaluator: { + evaluate: async () => { + throw new Error("an identical published snapshot must not be evaluated again"); + }, + }, + execution: execution.context, + updatedAt: now, + }), + ).resolves.toMatchObject({ + attempt: { checkpoint: "smoke_eval_passed" }, + evaluation: "previously-passed", + publication: { headRevision: 1, published: { id: conflictingPublicationId } }, + }); + await expect(publications.getPublished({ knowledgeSpaceId, tenantId })).resolves.toMatchObject({ + fingerprint, + headRevision: 1, + id: conflictingPublicationId, + }); + }); + it("requires server-side component validation before candidate creation or member mutation", async () => { const publications = createInMemoryProjectionSetPublicationRepository({ maxPublications: 10 }); const members = createInMemoryProjectionSetPublicationMemberRepository({ diff --git a/knowledge-fs/packages/api/src/document-compilation-publication-coordinator.ts b/knowledge-fs/packages/api/src/document-compilation-publication-coordinator.ts index b5cccb7b80b..4c18ae99a04 100644 --- a/knowledge-fs/packages/api/src/document-compilation-publication-coordinator.ts +++ b/knowledge-fs/packages/api/src/document-compilation-publication-coordinator.ts @@ -210,7 +210,10 @@ export function createDocumentCompilationPublicationCoordinator({ const initialAttempt = validateAttempt(input.execution.attempt); const deletionToken = await captureCompilationDeletionFence(deletionFence, initialAttempt); const assertWritable = () => assertCompilationDeletionFence(deletionFence, deletionToken); - const candidateId = normalizeUuid(input.candidateId); + const proposedCandidateId = normalizeUuid(input.candidateId); + const candidateId = initialAttempt.candidatePublicationId + ? normalizeUuid(initialAttempt.candidatePublicationId) + : proposedCandidateId; const createdAt = DateTimeSchema.parse(input.createdAt); const projectionVersion = positiveInteger(input.projectionVersion, "projectionVersion"); const fingerprintMaterial = ProjectionSetFingerprintMaterialSchema.parse( @@ -249,20 +252,48 @@ export function createDocumentCompilationPublicationCoordinator({ projectionVersion, tenantId: initialAttempt.tenantId, }); - assertCandidateIdentity(candidate, { + const reusesCurrentPublication = await isCurrentPublishedSnapshot(publications, candidate, { attempt: initialAttempt, - candidateId, fingerprint, projectionVersion, }); + if (!reusesCurrentPublication) { + assertCandidateIdentity(candidate, { + attempt: initialAttempt, + candidateId: proposedCandidateId, + fingerprint, + projectionVersion, + }); + } let attempt = validateAttempt(input.execution.attempt); assertSameAttemptScope(attempt, initialAttempt); if (!attempt.candidatePublicationId) { await assertWritable(); - attempt = await bindCandidate(input.execution, attempt, candidateId, fingerprint); + attempt = await bindCandidate(input.execution, attempt, candidate.id, fingerprint); } else { - assertAttemptCandidateBinding(attempt, candidateId, fingerprint); + assertAttemptCandidateBinding(attempt, candidate.id, fingerprint); + } + + if (reusesCurrentPublication) { + assertExecutionFence(input.execution); + await assertWritable(); + attempt = validateAttempt(await input.execution.heartbeat()); + assertSameAttemptScope(attempt, initialAttempt); + assertAttemptCandidateBinding(attempt, candidate.id, fingerprint); + if (attempt.checkpoint === "nodes_generated") { + attempt = await input.execution.advance({ + candidateFingerprint: fingerprint, + candidatePublicationId: candidate.id, + checkpoint: "projection_built", + }); + } + return { + attempt, + candidate, + inheritedMemberCount: 0, + replacedMemberCount: 0, + }; } assertExecutionFence(input.execution); @@ -345,13 +376,20 @@ export function createDocumentCompilationPublicationCoordinator({ "Document compilation candidate publication was not found", ); } - assertCandidateIdentity(candidate, { - allowedStatuses: ["candidate", "published"], + const reusesCurrentPublication = await isCurrentPublishedSnapshot(publications, candidate, { attempt: initialAttempt, - candidateId: candidatePublicationId, fingerprint: candidateFingerprint, projectionVersion: candidate.projectionVersion, }); + if (!reusesCurrentPublication) { + assertCandidateIdentity(candidate, { + allowedStatuses: ["candidate", "published"], + attempt: initialAttempt, + candidateId: candidatePublicationId, + fingerprint: candidateFingerprint, + projectionVersion: candidate.projectionVersion, + }); + } try { if (candidate.status === "published") { @@ -360,7 +398,8 @@ export function createDocumentCompilationPublicationCoordinator({ !published || published.id !== candidatePublicationId || published.fingerprint !== candidateFingerprint || - published.headRevision !== initialAttempt.baseHeadRevision + 1 + published.headRevision !== + initialAttempt.baseHeadRevision + (reusesCurrentPublication ? 0 : 1) ) { throw new DocumentCompilationCandidateIdentityConflictError( "Published document compilation candidate is not the expected publication head", @@ -514,6 +553,33 @@ export function createDocumentCompilationPublicationCoordinator({ }; } +async function isCurrentPublishedSnapshot( + publications: Pick, + candidate: ProjectionSetPublication, + expected: { + readonly attempt: DocumentCompilationAttempt; + readonly fingerprint: string; + readonly projectionVersion: number; + }, +): Promise { + if ( + candidate.status !== "published" || + candidate.fingerprint !== expected.fingerprint || + candidate.projectionVersion !== expected.projectionVersion + ) { + return false; + } + const published = await publications.getPublished({ + knowledgeSpaceId: expected.attempt.knowledgeSpaceId, + tenantId: expected.attempt.tenantId, + }); + return ( + published?.id === candidate.id && + published.fingerprint === expected.fingerprint && + published.headRevision === expected.attempt.baseHeadRevision + ); +} + async function ensureExclusiveCandidate( publications: Pick, input: Parameters[0], diff --git a/knowledge-fs/packages/api/src/document-write-handlers.ts b/knowledge-fs/packages/api/src/document-write-handlers.ts index 3c5529eaacd..47ce7fbe6a0 100644 --- a/knowledge-fs/packages/api/src/document-write-handlers.ts +++ b/knowledge-fs/packages/api/src/document-write-handlers.ts @@ -92,7 +92,11 @@ import { LegacySpacePublicationBootstrapSnapshotConflictError, withKnowledgeSpaceDocumentMutationLease, } from "./legacy-space-publication-bootstrap"; -import type { DocumentRevision, LogicalDocumentRepository } from "./logical-document-repository"; +import type { + DocumentRevision, + LogicalDocumentRepository, + LogicalDocumentWithActiveRevision, +} from "./logical-document-repository"; import { LogicalDocumentConflictError, LogicalDocumentNotFoundError, @@ -368,7 +372,10 @@ export function registerDocumentWriteHandlers({ const bulkJobId = generateBulkUploadId(); const items = []; const bulkItems: BulkOperationItem[] = []; - const enqueueAsset = async (asset: DocumentAsset) => { + const enqueueAsset = async ( + asset: DocumentAsset, + logicalDocument?: LogicalDocumentWithActiveRevision, + ) => { const compilationJob = await traceAsync( traces, traceId, @@ -387,7 +394,7 @@ export function registerDocumentWriteHandlers({ ); bulkItems.push({ compilationJobId: compilationJob.id, - documentId: asset.id, + documentId: logicalDocument?.id ?? asset.id, requiredPermissionScope: requiredPermissionScopeForAsset(asset), status: "queued", }); @@ -399,17 +406,34 @@ export function registerDocumentWriteHandlers({ stage: "queued" as const, }, status: "queued" as const, - statusUrl: createDocumentAssetStatusUrl({ documentAssetId: asset.id, knowledgeSpaceId }), + statusUrl: logicalDocument + ? createLogicalDocumentTaskStatusUrl({ + documentId: logicalDocument.id, + knowledgeSpaceId, + taskId: compilationJob.id, + }) + : createDocumentAssetStatusUrl({ documentAssetId: asset.id, knowledgeSpaceId }), }; }; for (const documentId of requestedDocumentIds ?? []) { - const asset = await traceAsync(traces, traceId, "ingestion.bulk_reindex_asset_lookup", () => - assets.get({ - id: documentId, - knowledgeSpaceId, - }), - ); + const logicalDocument = logicalDocuments + ? await logicalDocuments.get({ + documentId, + knowledgeSpaceId, + tenantId: subject.tenantId, + }) + : null; + const assetId = logicalDocument?.active?.documentAssetId ?? documentId; + const asset = + logicalDocument && !logicalDocument.active + ? null + : await traceAsync(traces, traceId, "ingestion.bulk_reindex_asset_lookup", () => + assets.get({ + id: assetId, + knowledgeSpaceId, + }), + ); if (!asset || !candidatePermissionAllowsAsset(asset, candidateGrants)) { items.push({ @@ -423,7 +447,7 @@ export function registerDocumentWriteHandlers({ continue; } - items.push(await enqueueAsset(asset)); + items.push(await enqueueAsset(asset, logicalDocument ?? undefined)); } for (const asset of selectedAssets?.items ?? []) { @@ -592,7 +616,6 @@ export function registerDocumentWriteHandlers({ let asset: DocumentAsset | undefined; let logicalRevision: DocumentRevision | undefined; let compilationJobId: string | undefined; - let pathCreated = false; try { await assertWritable(); @@ -640,17 +663,6 @@ export function registerDocumentWriteHandlers({ ); asset = createdAsset; createdAssets.push(createdAsset); - await assertWritable(); - await traceAsync(traces, traceId, "ingestion.bulk_document_path_upsert", () => - knowledgePaths.upsertMany([ - buildDocumentKnowledgePath({ - asset: createdAsset, - id: generateKnowledgePathId(), - tenantId: subject.tenantId, - }), - ]), - ); - pathCreated = true; logicalRevision = ( await traceAsync(traces, traceId, "ingestion.bulk_logical_revision_create", () => @@ -787,15 +799,6 @@ export function registerDocumentWriteHandlers({ // Once a revision exists, retain its raw asset as a failed, inspectable revision; only // this item is failed and previously accepted jobs keep their objects and queue state. if (!logicalRevision) { - if (pathCreated) { - await knowledgePaths - .deleteByDocumentAsset({ - documentAssetId: id, - knowledgeSpaceId, - maxPaths: 1, - }) - .catch(() => undefined); - } await scrubStaleDocumentUploadWithRetry( // The durable stale-write scrubber is intentionally deletion-fence-only. This // branch has already proved deletion did not win, so compensate the unpublished @@ -1261,16 +1264,18 @@ export function registerDocumentWriteHandlers({ asset = scopedAsset; metadataAsset = asset; } - await traceAsync(traces, traceId, "ingestion.document_path_upsert", () => - knowledgePaths.upsertMany([ - buildDocumentKnowledgePath({ - asset, - id: generateKnowledgePathId(), - tenantId: subject.tenantId, - }), - ]), - ); - metadataPathCreated = true; + if (!compilationAuthorization) { + await traceAsync(traces, traceId, "ingestion.document_path_upsert", () => + knowledgePaths.upsertMany([ + buildDocumentKnowledgePath({ + asset, + id: generateKnowledgePathId(), + tenantId: subject.tenantId, + }), + ]), + ); + metadataPathCreated = true; + } await assertWritable(); await traceAsync(traces, traceId, "ingestion.staged_commit_metadata_prepared", () => stagedCommits.transition({ diff --git a/knowledge-fs/packages/api/src/durable-deletion-repository.test.ts b/knowledge-fs/packages/api/src/durable-deletion-repository.test.ts index 597c149ad8e..f8afc209cc2 100644 --- a/knowledge-fs/packages/api/src/durable-deletion-repository.test.ts +++ b/knowledge-fs/packages/api/src/durable-deletion-repository.test.ts @@ -1430,7 +1430,10 @@ describe.each(["postgres", "tidb"] as const)( it("retries and then completes a fenced external item with redaction", async () => { const running = runningJobRow({ checkpoint: "deleting_objects" }); const itemId = "del-item-1"; - const pending = itemRow({ id: itemId }); + const pending = itemRow({ + id: itemId, + ...(dialect === "postgres" ? { ordinal: "1" } : {}), + }); const retryAt = "2026-07-14T12:01:00.000Z"; const retrying = itemRow({ attempts: 1, @@ -1467,7 +1470,7 @@ describe.each(["postgres", "tidb"] as const)( now: createdAt, }), ).resolves.toMatchObject([ - { attempts: 0, id: itemId, objectKey: pending.object_key, status: "pending" }, + { attempts: 0, id: itemId, objectKey: pending.object_key, ordinal: 1, status: "pending" }, ]); claimScript.expectDone(); @@ -1515,11 +1518,13 @@ describe.each(["postgres", "tidb"] as const)( redactedAt: completedAt, status: "completed", }); - expect( - completeScript.calls.find( - (call) => call.operation === "update" && call.tableName === "deletion_job_items", - )?.sql, - ).toContain("redacted_at"); + const completeItemSql = completeScript.calls.find( + (call) => call.operation === "update" && call.tableName === "deletion_job_items", + )?.sql; + expect(completeItemSql).toContain("redacted_at"); + if (dialect === "postgres") { + expect(completeItemSql).toContain("THEN CAST($3 AS TIMESTAMPTZ)"); + } completeScript.expectDone(); }); diff --git a/knowledge-fs/packages/api/src/durable-deletion-repository.ts b/knowledge-fs/packages/api/src/durable-deletion-repository.ts index 2683c3cb1da..bd0e7341eb7 100644 --- a/knowledge-fs/packages/api/src/durable-deletion-repository.ts +++ b/knowledge-fs/packages/api/src/durable-deletion-repository.ts @@ -14,6 +14,7 @@ import { resolveCapabilityJobPublicationGrant, } from "./capability-job-fence"; import { + nonnegativeSafeIntegerColumn, numberColumn, optionalNumberColumn, optionalStringColumn, @@ -1299,11 +1300,13 @@ async function completeDeletionItem( ) { return null; } + const completionTimestamp = + database.dialect === "postgres" ? `CAST(${p(database, 3)} AS TIMESTAMPTZ)` : p(database, 3); const updated = await transaction.execute({ maxRows: 0, operation: "update", params: [item.attempts + 1, item.rowVersion + 1, input.now, item.id, job.id, item.rowVersion], - sql: `UPDATE ${q(database, itemTable)} SET ${q(database, "status")} = 'completed', ${q(database, "attempts")} = ${p(database, 1)}, ${q(database, "next_attempt_at")} = NULL, ${q(database, "object_key")} = NULL, ${q(database, "credential_ref")} = NULL, ${q(database, "cache_key")} = NULL, ${q(database, "last_error_code")} = NULL, ${q(database, "last_error_message")} = NULL, ${q(database, "row_version")} = ${p(database, 2)}, ${q(database, "updated_at")} = ${p(database, 3)}, ${q(database, "completed_at")} = ${p(database, 3)}, ${q(database, "redacted_at")} = CASE WHEN ${q(database, "kind")} IN ('object', 'secret_ref', 'cache_key') THEN ${p(database, 3)} ELSE NULL END WHERE ${q(database, "id")} = ${p(database, 4)} AND ${q(database, "deletion_job_id")} = ${p(database, 5)} AND ${q(database, "row_version")} = ${p(database, 6)};`, + sql: `UPDATE ${q(database, itemTable)} SET ${q(database, "status")} = 'completed', ${q(database, "attempts")} = ${p(database, 1)}, ${q(database, "next_attempt_at")} = NULL, ${q(database, "object_key")} = NULL, ${q(database, "credential_ref")} = NULL, ${q(database, "cache_key")} = NULL, ${q(database, "last_error_code")} = NULL, ${q(database, "last_error_message")} = NULL, ${q(database, "row_version")} = ${p(database, 2)}, ${q(database, "updated_at")} = ${completionTimestamp}, ${q(database, "completed_at")} = ${completionTimestamp}, ${q(database, "redacted_at")} = CASE WHEN ${q(database, "kind")} IN ('object', 'secret_ref', 'cache_key') THEN ${completionTimestamp} ELSE NULL END WHERE ${q(database, "id")} = ${p(database, 4)} AND ${q(database, "deletion_job_id")} = ${p(database, 5)} AND ${q(database, "row_version")} = ${p(database, 6)};`, tableName: itemTable, }); return updated.rowsAffected === 1 @@ -3165,7 +3168,7 @@ function mapItem(row: DatabaseRow): DurableDeletionJobItem { ...(optionalStringColumn(row, "object_key") ? { objectKey: optionalStringColumn(row, "object_key") } : {}), - ordinal: numberColumn(row, "ordinal"), + ordinal: nonnegativeSafeIntegerColumn(row, "ordinal"), payloadDigest: stringColumn(row, "payload_digest"), ...(optionalStringColumn(row, "redacted_at") ? { redactedAt: optionalStringColumn(row, "redacted_at") } diff --git a/knowledge-fs/packages/api/src/gateway-document-write.test.ts b/knowledge-fs/packages/api/src/gateway-document-write.test.ts index 104177f236f..fa3a947543e 100644 --- a/knowledge-fs/packages/api/src/gateway-document-write.test.ts +++ b/knowledge-fs/packages/api/src/gateway-document-write.test.ts @@ -3473,6 +3473,89 @@ describe("document write gateway integration", () => { ).toThrow("Bulk document reindex maxBulkReindexDocuments must be at least 1"); }); + it("resolves a logical document id to its active asset for reindexing", async () => { + const knowledgeSpaceId = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c42"; + const logicalDocumentId = "018f0d60-7a49-7cc2-9c1b-5b36f18f3d01"; + const assetId = "018f0d60-7a49-7cc2-9c1b-5b36f18f3a01"; + const adapter = createNodePlatformAdapter({ env: {} }); + const assets = createInMemoryDocumentAssetRepository({ maxAssets: 10 }); + const asset = await assets.create({ + filename: "Logical.md", + id: assetId, + knowledgeSpaceId, + mimeType: "text/markdown", + objectKey: "tenant-1/spaces/space/documents/logical.md", + sha256: "a".repeat(64), + sizeBytes: 1, + }); + const logicalDocuments = createInMemoryLogicalDocumentRepository({ + canReadDocument: ({ candidateGrants }) => candidateGrants.includes("document:read"), + canReadRevision: ({ candidateGrants }) => candidateGrants.includes("document:read"), + generateDocumentId: () => logicalDocumentId, + maxDocuments: 10, + maxRevisionsPerDocument: 2, + }); + const candidate = await logicalDocuments.createCandidateRevision({ + contentHash: "a".repeat(64), + documentAssetId: asset.id, + documentAssetVersion: asset.version, + knowledgeSpaceId, + mimeType: asset.mimeType, + now: "2026-07-27T00:00:00.000Z", + sizeBytes: asset.sizeBytes, + systemMetadata: {}, + tenantId: "tenant-1", + title: asset.filename, + }); + await logicalDocuments.activateRevision({ + documentId: logicalDocumentId, + expectedActiveRevision: null, + expectedRowVersion: 0, + knowledgeSpaceId, + now: "2026-07-27T00:01:00.000Z", + revision: candidate.revision.revision, + tenantId: "tenant-1", + }); + const compilationJobs = createDocumentCompilationJobStateMachine({ + generateId: () => "logical-reindex-job-1", + jobs: adapter.jobs, + repository: createInMemoryDocumentCompilationJobRepository({ maxJobs: 10 }), + }); + const app = createKnowledgeGateway({ + adapter, + auth: createTestAuthVerifier(), + documentAssets: assets, + documentCompilationJobs: compilationJobs, + generateBulkUploadId: () => "logical-reindex-bulk-1", + knowledgeSpaces: createInMemoryKnowledgeSpaceRepository({ + generateId: () => knowledgeSpaceId, + maxListLimit: 10, + maxSpaces: 10, + }), + logicalDocuments, + }); + await app.request("/knowledge-spaces", { + body: JSON.stringify({ name: "Logical reindex", slug: "logical-reindex" }), + headers: { ...bearer(writeToken), "content-type": "application/json" }, + method: "POST", + }); + + const response = await app.request( + `/knowledge-spaces/${knowledgeSpaceId}/documents/bulk/reindex`, + { + body: JSON.stringify({ documentIds: [logicalDocumentId] }), + headers: { ...bearer(writeToken), "content-type": "application/json" }, + method: "POST", + }, + ); + + expect(response.status).toBe(202); + await expect(response.json()).resolves.toMatchObject({ + items: [{ asset: { id: assetId }, status: "queued" }], + total: 1, + }); + }); + it("reports tenant-scoped bulk job progress across queued and completed operations", async () => { const adapter = createNodePlatformAdapter({ env: {} }); const assets = createInMemoryDocumentAssetRepository({ diff --git a/knowledge-fs/packages/api/src/gateway.test.ts b/knowledge-fs/packages/api/src/gateway.test.ts index baba587950e..936a9ebcd75 100644 --- a/knowledge-fs/packages/api/src/gateway.test.ts +++ b/knowledge-fs/packages/api/src/gateway.test.ts @@ -9874,27 +9874,6 @@ describe("createKnowledgeGateway", () => { nodes, }), ).toThrow("Incremental reindexer maxNodes must be at least 1"); - await expect( - createIncrementalReindexer({ - artifacts, - compute, - denseBuilder: { - build: async () => [], - }, - maxNodes: 4, - nodes, - }).reindex({ - knowledgeSpaceId, - parseArtifact: ParseArtifactSchema.parse({ - ...changedArtifact, - artifactHash: "c".repeat(64), - }), - projectionVersion: 2, - }), - ).rejects.toThrow( - "Incremental reindexer denseModel is required when denseBuilder is configured", - ); - await nodes.deleteByDocumentAsset({ documentAssetId, knowledgeSpaceId, maxNodes: 4 }); const denseBuilds: unknown[] = []; await expect( diff --git a/knowledge-fs/packages/api/src/index-reindexer.test.ts b/knowledge-fs/packages/api/src/index-reindexer.test.ts index 0c015216a53..5a8debd4ae6 100644 --- a/knowledge-fs/packages/api/src/index-reindexer.test.ts +++ b/knowledge-fs/packages/api/src/index-reindexer.test.ts @@ -623,7 +623,7 @@ describe("incremental reindexer", () => { ).rejects.toThrow("inconsistent text embedding space"); }); - it("validates bounded configuration, dense model requirements, and max node output", async () => { + it("validates bounded configuration, optional dense indexing, and max node output", async () => { const artifacts = createInMemoryParseArtifactRepository({ maxArtifacts: 4 }); const nodes = createInMemoryKnowledgeNodeRepository({ maxBatchSize: 4, @@ -641,11 +641,17 @@ describe("incremental reindexer", () => { }), ).toThrow("Incremental reindexer maxNodes must be at least 1"); + let denseBuilds = 0; await expect( createIncrementalReindexer({ artifacts, compute, - denseBuilder: { build: async () => [] }, + denseBuilder: { + build: async () => { + denseBuilds += 1; + return []; + }, + }, maxNodes: 4, nodes, }).reindex({ @@ -653,9 +659,8 @@ describe("incremental reindexer", () => { parseArtifact: parseArtifact({ artifactHash: "c".repeat(64) }), projectionVersion: 1, }), - ).rejects.toThrow( - "Incremental reindexer denseModel is required when denseBuilder is configured", - ); + ).resolves.toMatchObject({ status: "rebuilt" }); + expect(denseBuilds).toBe(0); await expect( createIncrementalReindexer({ diff --git a/knowledge-fs/packages/api/src/index-reindexer.ts b/knowledge-fs/packages/api/src/index-reindexer.ts index e99ca98478d..3f26e8f4196 100644 --- a/knowledge-fs/packages/api/src/index-reindexer.ts +++ b/knowledge-fs/packages/api/src/index-reindexer.ts @@ -161,7 +161,7 @@ export function createIncrementalReindexer({ } : {}), reindex: async (input) => { - validateIncrementalReindexInput(input, { denseBuilder, visualBuilder }); + validateIncrementalReindexInput(input, { visualBuilder }); const parseArtifact = cloneParseArtifact(ParseArtifactSchema.parse(input.parseArtifact)); const publicationGenerationId = input.publicationGenerationId === undefined @@ -375,10 +375,7 @@ function validateReindexProjectionDimensions( function validateIncrementalReindexInput( input: IncrementalReindexInput, - { - denseBuilder, - visualBuilder, - }: Pick, + { visualBuilder }: Pick, ): void { if (!input.knowledgeSpaceId.trim()) { throw new Error("Incremental reindexer knowledgeSpaceId is required"); @@ -396,10 +393,6 @@ function validateIncrementalReindexInput( PublicationGenerationIdSchema.parse(input.publicationGenerationId); } - if (denseBuilder && !input.denseModel?.trim()) { - throw new Error("Incremental reindexer denseModel is required when denseBuilder is configured"); - } - if (visualBuilder && !input.visualModel?.trim()) { throw new Error( "Incremental reindexer visualModel is required when visualBuilder is configured", diff --git a/knowledge-fs/packages/api/src/logical-document-repository.ts b/knowledge-fs/packages/api/src/logical-document-repository.ts index f909e19afa1..36aa26aac19 100644 --- a/knowledge-fs/packages/api/src/logical-document-repository.ts +++ b/knowledge-fs/packages/api/src/logical-document-repository.ts @@ -7,6 +7,7 @@ import { import { CapabilityPublicationFencedError } from "./capability-grant-provenance"; import { resolveCapabilityJobPublicationGrant } from "./capability-job-fence"; import { + nonnegativeSafeIntegerColumn, numberColumn, optionalNumberColumn, optionalStringColumn, @@ -2218,7 +2219,7 @@ function mapRevision(row: DatabaseRow): DocumentRevision { knowledgeSpaceId: stringColumn(row, "knowledge_space_id"), mimeType: stringColumn(row, "mime_type"), revision: numberColumn(row, "revision"), - sizeBytes: numberColumn(row, "size_bytes"), + sizeBytes: nonnegativeSafeIntegerColumn(row, "size_bytes"), state, systemMetadata: jsonObjectColumn(row, "system_metadata"), tenantId: stringColumn(row, "tenant_id"), diff --git a/knowledge-fs/packages/api/src/page-index-build-repository.test.ts b/knowledge-fs/packages/api/src/page-index-build-repository.test.ts index 654427e0e69..ef8f0c739c3 100644 --- a/knowledge-fs/packages/api/src/page-index-build-repository.test.ts +++ b/knowledge-fs/packages/api/src/page-index-build-repository.test.ts @@ -161,13 +161,13 @@ describe("flattened PageIndex build repository", () => { rows: [ { checksum: manifestParams[10], - actual_node_count: nodeRows.length, - actual_term_count: termRows.length, + actual_node_count: String(nodeRows.length), + actual_term_count: String(termRows.length), document_asset_id: manifestParams[3], document_outline_id: manifestParams[4], document_version: manifestParams[5], id: manifestParams[0], - invalid_term_count: 0, + invalid_term_count: "0", knowledge_space_id: manifestParams[1], node_count: manifestParams[8], publication_generation_id: manifestParams[2], diff --git a/knowledge-fs/packages/api/src/page-index-build-repository.ts b/knowledge-fs/packages/api/src/page-index-build-repository.ts index 8b8c07a0ee6..0173d04d339 100644 --- a/knowledge-fs/packages/api/src/page-index-build-repository.ts +++ b/knowledge-fs/packages/api/src/page-index-build-repository.ts @@ -15,7 +15,12 @@ import { } from "@knowledge/core"; import { deterministicChildId } from "./api-shared-utils"; -import { numberColumn, optionalStringColumn, stringColumn } from "./database-row-utils"; +import { + nonnegativeSafeIntegerColumn, + numberColumn, + optionalStringColumn, + stringColumn, +} from "./database-row-utils"; import { databasePlaceholder, quoteDatabaseIdentifier } from "./database-sql-utils"; import { jsonArrayColumn, jsonObjectColumn } from "./json-utils"; import { @@ -745,9 +750,9 @@ async function readLockedDatabasePageIndex( ) as typeof PageIndexTokenizerVersion, }; return { - actualNodeCount: numberColumn(manifestRow, "actual_node_count"), - actualTermCount: numberColumn(manifestRow, "actual_term_count"), - invalidTermCount: numberColumn(manifestRow, "invalid_term_count"), + actualNodeCount: nonnegativeSafeIntegerColumn(manifestRow, "actual_node_count"), + actualTermCount: nonnegativeSafeIntegerColumn(manifestRow, "actual_term_count"), + invalidTermCount: nonnegativeSafeIntegerColumn(manifestRow, "invalid_term_count"), manifest, }; } diff --git a/knowledge-fs/packages/api/src/source-product-routes.ts b/knowledge-fs/packages/api/src/source-product-routes.ts index fa704401e66..ac65c09c4ba 100644 --- a/knowledge-fs/packages/api/src/source-product-routes.ts +++ b/knowledge-fs/packages/api/src/source-product-routes.ts @@ -71,6 +71,7 @@ const BulkWorkflowItem = z.object({ export const listSourceProvidersRoute = createRoute({ method: "get", + operationId: "listSourceProviders", path: "/source-providers", responses: { 200: { @@ -83,6 +84,7 @@ export const listSourceProvidersRoute = createRoute({ export const createSourceConnectionRoute = createRoute({ method: "post", + operationId: "createSourceConnection", path: "/knowledge-spaces/{id}/source-connections", request: { params: SpaceParams, @@ -189,6 +191,7 @@ export const completeSourceOAuthRoute = createRoute({ export const listSourceConnectionsRoute = createRoute({ method: "get", + operationId: "listSourceConnections", path: "/knowledge-spaces/{id}/source-connections", request: { params: SpaceParams, @@ -231,6 +234,7 @@ export const getSourceConnectionRoute = createRoute({ export const refreshSourceConnectionRoute = createRoute({ method: "post", + operationId: "refreshSourceConnection", path: "/knowledge-spaces/{id}/source-connections/{connectionId}/refresh", request: { params: ConnectionParams, @@ -277,6 +281,7 @@ export const revokeSourceConnectionRoute = createRoute({ export const createSourceSyncWorkflowRoute = createRoute({ method: "post", + operationId: "createSourceSyncWorkflow", path: "/knowledge-spaces/{id}/sources/{sourceId}/sync", request: { params: SourceParams, headers: IdempotencyHeader }, responses: { @@ -294,6 +299,7 @@ export const createSourceSyncWorkflowRoute = createRoute({ export const createSourceCrawlPreviewWorkflowRoute = createRoute({ method: "post", + operationId: "createSourceCrawlPreviewWorkflow", path: "/knowledge-spaces/{id}/sources/{sourceId}/crawl-preview", request: { params: SourceParams, headers: IdempotencyHeader }, responses: { @@ -374,6 +380,7 @@ export const createSourceImportWorkflowRoute = createRoute({ export const getSourceSyncPolicyRoute = createRoute({ method: "get", + operationId: "getSourceSyncPolicy", path: "/knowledge-spaces/{id}/sources/{sourceId}/sync-policy", request: { params: SourceParams }, responses: { @@ -389,6 +396,7 @@ export const getSourceSyncPolicyRoute = createRoute({ export const putSourceSyncPolicyRoute = createRoute({ method: "put", + operationId: "putSourceSyncPolicy", path: "/knowledge-spaces/{id}/sources/{sourceId}/sync-policy", request: { params: SourceParams, @@ -487,6 +495,7 @@ export const listSourceWorkflowsRoute = createRoute({ export const getSourceWorkflowRoute = createRoute({ method: "get", + operationId: "getSourceWorkflow", path: "/knowledge-spaces/{id}/source-workflows/{runId}", request: { params: WorkflowParams }, responses: { @@ -532,6 +541,7 @@ export const listSourceBulkWorkflowItemsRoute = createRoute({ export const cancelSourceWorkflowRoute = createRoute({ method: "post", + operationId: "cancelSourceWorkflow", path: "/knowledge-spaces/{id}/source-workflows/{runId}/cancel", request: { params: WorkflowParams, @@ -558,6 +568,7 @@ export const cancelSourceWorkflowRoute = createRoute({ export const retrySourceWorkflowRoute = createRoute({ method: "post", + operationId: "retrySourceWorkflow", path: "/knowledge-spaces/{id}/source-workflows/{runId}/retry", request: { params: WorkflowParams }, responses: { @@ -574,6 +585,7 @@ export const retrySourceWorkflowRoute = createRoute({ export const listCrawlPreviewPagesRoute = createRoute({ method: "get", + operationId: "listCrawlPreviewPages", path: "/knowledge-spaces/{id}/source-workflows/{runId}/pages", request: { params: WorkflowParams, @@ -612,6 +624,7 @@ export const listCrawlPreviewPagesRoute = createRoute({ export const selectCrawlPreviewPagesRoute = createRoute({ method: "post", + operationId: "selectCrawlPreviewPages", path: "/knowledge-spaces/{id}/source-workflows/{runId}/selection", request: { params: WorkflowParams, diff --git a/knowledge-fs/packages/api/src/source-product-workflow-database-repository.test.ts b/knowledge-fs/packages/api/src/source-product-workflow-database-repository.test.ts index aca3f0a8f05..dd6915c3af9 100644 --- a/knowledge-fs/packages/api/src/source-product-workflow-database-repository.test.ts +++ b/knowledge-fs/packages/api/src/source-product-workflow-database-repository.test.ts @@ -209,6 +209,127 @@ describe.each(["postgres", "tidb"] as const)( ); }); + it("attributes capability workflow completion to the admitted grant subject", async () => { + const calls: DatabaseExecuteInput[] = []; + const database = orderedMutationDatabase( + dialect, + calls, + runningCapabilitySourceRunRow(), + false, + ); + const repository = createDatabaseSourceProductWorkflowRepository({ database }); + + await expect( + repository.complete({ + fence: { leaseToken, rowVersion: 7, runId, workerId: "source-worker" }, + now, + }), + ).resolves.toMatchObject({ state: "completed" }); + + const activity = calls.find( + (call) => + call.tableName === "knowledge_space_activity_events" && call.operation === "insert", + ); + expect(activity?.params.slice(3, 5)).toEqual(["member", "editor-a"]); + }); + + it("attributes capability workflow failure to the admitted grant subject", async () => { + const calls: DatabaseExecuteInput[] = []; + const database = orderedMutationDatabase( + dialect, + calls, + runningCapabilitySourceRunRow(), + false, + ); + const repository = createDatabaseSourceProductWorkflowRepository({ database }); + + await expect( + repository.fail({ + errorCode: "SOURCE_IMPORT_FAILED", + errorMessage: "provider unavailable", + fence: { leaseToken, rowVersion: 7, runId, workerId: "source-worker" }, + now, + }), + ).resolves.toMatchObject({ state: "failed" }); + + const activity = calls.find( + (call) => + call.tableName === "knowledge_space_activity_events" && call.operation === "insert", + ); + expect(activity?.params.slice(3, 5)).toEqual(["member", "editor-a"]); + }); + + it("keeps capability workflow update columns aligned with their parameters", async () => { + const calls: DatabaseExecuteInput[] = []; + const database = orderedMutationDatabase(dialect, calls, capabilitySourceRunRow(), false); + const repository = createDatabaseSourceProductWorkflowRepository({ database }); + + await repository.cancel({ + capabilityGrantId, + now, + reason: "stop", + runId, + }); + + const update = calls.find( + (call) => call.tableName === "source_workflow_runs" && call.operation === "update", + ); + expect(update?.sql).toContain("capability_grant_id"); + expect(update?.params[11]).toBe(capabilityGrantId); + const placeholders = update?.sql.match(dialect === "postgres" ? /\$\d+/gu : /\?/gu) ?? []; + expect(placeholders).toHaveLength(update?.params.length ?? 0); + }); + + it("terminalizes a fenced worker failure while durable deletion is active", async () => { + const calls: DatabaseExecuteInput[] = []; + const database = orderedMutationDatabase(dialect, calls, runRow(), false, true); + const repository = createDatabaseSourceProductWorkflowRepository({ database }); + + await expect( + repository.fail({ + errorCode: "SOURCE_IMPORT_PARTIAL_FAILURE", + errorMessage: "Import compensation requested deletion", + fence: { leaseToken, rowVersion: 7, runId, workerId: "source-worker" }, + now, + }), + ).resolves.toMatchObject({ + activeSlot: undefined, + state: "failed", + }); + expect( + calls.some( + (call) => call.tableName === "source_workflow_runs" && call.operation === "update", + ), + ).toBe(true); + }); + + it("cancels an authorized source workflow while durable deletion is active", async () => { + const calls: DatabaseExecuteInput[] = []; + const database = orderedMutationDatabase( + dialect, + calls, + sourceRunRow("running"), + false, + true, + ); + const repository = createDatabaseSourceProductWorkflowRepository({ database }); + + await expect( + repository.cancel({ + accessChannel: "interactive", + now, + permissionSnapshotId, + permissionSnapshotRevision: 1, + reason: "stop", + requestedBySubjectId: "editor-a", + runId, + }), + ).resolves.toMatchObject({ + activeSlot: undefined, + state: "canceled", + }); + }); + it("revalidates capability source workflows at restart and terminals revoked work", async () => { const build = (active: boolean) => { const calls: DatabaseExecuteInput[] = []; @@ -1863,6 +1984,7 @@ function claimDatabase( if (input.tableName === "knowledge_space_permission_snapshots") { return { rows: [permissionRow()], rowsAffected: 1 }; } + if (input.tableName === "capability_grants") return activeCapabilityGrant(); if (isAccessLock(input.tableName)) return oneRow(input.tableName); if (input.tableName === "sources") return { rows: [sourceRow([])], rowsAffected: 1 }; if (input.tableName === "source_workflow_outbox" && input.operation === "select") { @@ -2138,6 +2260,18 @@ function capabilitySourceRunRow(): DatabaseRow { }; } +function runningCapabilitySourceRunRow(): DatabaseRow { + return { + ...sourceRunRow("running"), + access_channel: null, + capability_grant_id: capabilityGrantId, + permission_snapshot_id: null, + permission_snapshot_revision: null, + requested_by_subject_id: null, + required_permission_scope: null, + }; +} + function newBulkRun(): NewSourceWorkflowRun { return { accessChannel: "interactive", @@ -2262,14 +2396,19 @@ function orderedMutationDatabase( calls: DatabaseExecuteInput[], row: DatabaseRow, idempotencyMiss: boolean, + activeDeletion = false, ): DatabaseAdapter { + let storedActivity: DatabaseRow | undefined; return testDatabase(dialect, async (input) => { calls.push(input); if (input.tableName === "knowledge_spaces") return activeSpace(); - if (input.tableName === "deletion_jobs") return empty(); + if (input.tableName === "deletion_jobs") { + return activeDeletion ? oneRow("deletion_jobs") : empty(); + } if (input.tableName === "knowledge_space_permission_snapshots") { return { rows: [permissionRow()], rowsAffected: 1 }; } + if (input.tableName === "capability_grants") return activeCapabilityGrant(); if (isAccessLock(input.tableName)) return oneRow(input.tableName); if (input.tableName === "sources") { return { rows: [sourceRow([])], rowsAffected: 1 }; @@ -2286,6 +2425,13 @@ function orderedMutationDatabase( if (input.tableName === "source_crawl_preview_pages" && input.operation === "select") { return { rows: [{ page_id: "page-a" }], rowsAffected: 1 }; } + if (input.tableName === "knowledge_space_activity_events") { + if (input.operation === "insert") { + storedActivity = activityRow(input.params); + return { rows: [], rowsAffected: 1 }; + } + return storedActivity ? { rows: [storedActivity], rowsAffected: 1 } : empty(); + } return { rows: [], rowsAffected: 1 }; }); } diff --git a/knowledge-fs/packages/api/src/source-product-workflow-database-repository.ts b/knowledge-fs/packages/api/src/source-product-workflow-database-repository.ts index 2d1662f3650..326315cb067 100644 --- a/knowledge-fs/packages/api/src/source-product-workflow-database-repository.ts +++ b/knowledge-fs/packages/api/src/source-product-workflow-database-repository.ts @@ -609,7 +609,7 @@ export function createDatabaseSourceProductWorkflowRepository(input: { }), complete: ({ fence, now, state = "completed" }) => database.transaction(async (tx) => { - const { run: current } = await requireFenced(database, tx, fence, now); + const { permission, run: current } = await requireFenced(database, tx, fence, now); if (state === "preview_ready" && current.kind !== "crawl-preview") invalidState(); const terminal = state === "completed" || state === "zero_results"; const next = await writeFenced(database, tx, current, { @@ -631,7 +631,15 @@ export function createDatabaseSourceProductWorkflowRepository(input: { }); await finishOutbox(database, tx, current.id, "completed", now); if (terminal && current.kind === "sync" && current.sourceId) { - await appendSourceWorkflowActivity(database, tx, next, "source.synced", "success", now); + await appendSourceWorkflowActivity( + database, + tx, + next, + "source.synced", + "success", + now, + permission?.actorSubjectId, + ); } return next; }), @@ -662,7 +670,16 @@ export function createDatabaseSourceProductWorkflowRepository(input: { }), fail: ({ errorCode, errorMessage, fence, now }) => database.transaction(async (tx) => { - const { run: current } = await requireFenced(database, tx, fence, now, [], true); + const { permission, run: current } = await requireFenced( + database, + tx, + fence, + now, + [], + true, + true, + true, + ); const next = await writeTerminal(database, tx, current, { errorCode, errorMessage, @@ -671,7 +688,15 @@ export function createDatabaseSourceProductWorkflowRepository(input: { }); await finishOutbox(database, tx, current.id, "completed", now); if (current.sourceId) { - await appendSourceWorkflowActivity(database, tx, next, "source.failed", "failure", now); + await appendSourceWorkflowActivity( + database, + tx, + next, + "source.failed", + "failure", + now, + permission?.actorSubjectId, + ); } return next; }), @@ -686,13 +711,22 @@ export function createDatabaseSourceProductWorkflowRepository(input: { runId, }) => database.transaction(async (tx) => { - const admitted = await getRunForMutationAdmission(database, tx, runId, now, { - accessChannel, - capabilityGrantId, - permissionSnapshotId, - permissionSnapshotRevision, - requestedBySubjectId, - }); + const admitted = await getRunForMutationAdmission( + database, + tx, + runId, + now, + { + accessChannel, + capabilityGrantId, + permissionSnapshotId, + permissionSnapshotRevision, + requestedBySubjectId, + }, + [], + false, + true, + ); if (!admitted) return null; const current = admitted.run; if (["completed", "zero_results", "canceled"].includes(current.state)) return current; @@ -1444,6 +1478,7 @@ async function updateRun( "progress_completed", "progress_skipped", "progress_failed", + "capability_grant_id", "permission_snapshot_id", "permission_snapshot_revision", "requested_by_subject_id", @@ -1465,8 +1500,8 @@ async function updateRun( "required_permission_scope", ] as const; const allParams = runParams(next); - // Immutable id/tenant/space occupy 0..2 and created_at is immutable at index 29. - const sourceParams = [...allParams.slice(3, 29), ...allParams.slice(30)]; + // Immutable id/tenant/space occupy 0..2 and created_at is immutable at index 30. + const sourceParams = [...allParams.slice(3, 30), ...allParams.slice(31)]; const updateParams = [...sourceParams, next.id, next.rowVersion - 1, ...extraParams]; const idPosition = mutableColumns.length + 1; const versionPosition = idPosition + 1; @@ -1489,6 +1524,8 @@ async function requireFenced( now: string, additionalSourceIds: readonly string[] = [], allowInvalidPermission = false, + allowDeletionFencedTerminalization = false, + bypassAuthorizationThroughDeletionFence = false, ) { const admitted = await getRunForMutationAdmission( database, @@ -1498,6 +1535,8 @@ async function requireFenced( undefined, additionalSourceIds, allowInvalidPermission, + allowDeletionFencedTerminalization, + bypassAuthorizationThroughDeletionFence, ); const run = admitted?.run; if ( @@ -1526,6 +1565,8 @@ async function getRunForMutationAdmission( >, additionalSourceIds: readonly string[] = [], allowInvalidPermission = false, + allowDeletionFencedTerminalization = false, + bypassAuthorizationThroughDeletionFence = false, ): Promise<{ readonly permission: SourceWorkflowAuthorization | undefined; readonly run: SourceWorkflowRun; @@ -1533,7 +1574,12 @@ async function getRunForMutationAdmission( } | null> { const candidate = await getRun(database, tx, runId, false); if (!candidate) return null; - if (!(await lockKnowledgeSpaceForDeletionAdmission(database, tx, candidate))) { + const writable = await lockKnowledgeSpaceForDeletionAdmission(database, tx, candidate); + const terminalizingThroughDeletionFence = !writable && allowDeletionFencedTerminalization; + if ( + !writable && + (!terminalizingThroughDeletionFence || !(await knowledgeSpaceExists(database, tx, candidate))) + ) { throw new SourceWorkflowError( "SOURCE_WORKFLOW_SPACE_NOT_WRITABLE", "Knowledge space is missing or deletion-fenced", @@ -1543,43 +1589,53 @@ async function getRunForMutationAdmission( ? { ...candidate, ...authorizationOverride } : candidate; let permission: SourceWorkflowAuthorization | undefined; - try { - permission = await assertSourceWorkflowPermissionFence(database, tx, authorizationBinding, now); - } catch (error) { - if ( - !allowInvalidPermission || - !(error instanceof SourceWorkflowError) || - error.code !== "SOURCE_WORKFLOW_PERMISSION_INVALID" - ) { - throw error; - } - } let sourceScopes: ReadonlyMap; - try { - sourceScopes = await lockSourceWorkflowAdmissions( - database, - tx, - candidate.knowledgeSpaceId, - [candidate.sourceId, ...additionalSourceIds], - permission, - ); - } catch (error) { - if ( - !allowInvalidPermission || - !(error instanceof SourceWorkflowError) || - error.code !== "SOURCE_WORKFLOW_PERMISSION_INVALID" - ) { - throw error; - } + if (terminalizingThroughDeletionFence && bypassAuthorizationThroughDeletionFence) { permission = undefined; - sourceScopes = await lockSourceWorkflowAdmissions( - database, - tx, - candidate.knowledgeSpaceId, - [candidate.sourceId, ...additionalSourceIds], - undefined, - true, - ); + sourceScopes = new Map(); + } else { + try { + permission = await assertSourceWorkflowPermissionFence( + database, + tx, + authorizationBinding, + now, + ); + } catch (error) { + if ( + !allowInvalidPermission || + !(error instanceof SourceWorkflowError) || + error.code !== "SOURCE_WORKFLOW_PERMISSION_INVALID" + ) { + throw error; + } + } + try { + sourceScopes = await lockSourceWorkflowAdmissions( + database, + tx, + candidate.knowledgeSpaceId, + [candidate.sourceId, ...additionalSourceIds], + permission, + ); + } catch (error) { + if ( + !allowInvalidPermission || + !(error instanceof SourceWorkflowError) || + error.code !== "SOURCE_WORKFLOW_PERMISSION_INVALID" + ) { + throw error; + } + permission = undefined; + sourceScopes = await lockSourceWorkflowAdmissions( + database, + tx, + candidate.knowledgeSpaceId, + [candidate.sourceId, ...additionalSourceIds], + undefined, + true, + ); + } } const current = await getRun(database, tx, runId, true); if (!current) return null; @@ -1600,6 +1656,21 @@ async function getRunForMutationAdmission( return { permission, run: current, sourceScopes }; } +async function knowledgeSpaceExists( + database: DatabaseAdapter, + tx: DatabaseExecutor, + input: Pick, +): Promise { + const result = await tx.execute({ + maxRows: 1, + operation: "select", + params: [input.tenantId, input.knowledgeSpaceId], + sql: `SELECT ${q(database, "id")} FROM ${q(database, "knowledge_spaces")} WHERE ${q(database, "tenant_id")} = ${p(database, 1)} AND ${q(database, "id")} = ${p(database, 2)} LIMIT 1;`, + tableName: "knowledge_spaces", + }); + return result.rows.length === 1; +} + async function lockSourceWorkflowAdmissions( database: DatabaseAdapter, tx: DatabaseExecutor, @@ -1701,7 +1772,10 @@ async function assertSourceWorkflowPermissionFence( knowledgeSpaceId: binding.knowledgeSpaceId, tenantId: binding.tenantId, }); - return { permissionScopes: grant.contentScopeIds }; + return { + actorSubjectId: grant.subjectId, + permissionScopes: grant.contentScopeIds, + }; } catch { throw new SourceWorkflowError( "SOURCE_WORKFLOW_PERMISSION_INVALID", @@ -1745,10 +1819,15 @@ async function assertSourceWorkflowPermissionFence( ); } assertSourceWorkflowScopeAllowed(binding.requiredPermissionScope, permission.permissionScopes); - return permission; + return { + actorSubjectId: permission.subjectId, + permissionScopes: permission.permissionScopes, + }; } interface SourceWorkflowAuthorization { + /** Subject resolved under the same durable permission or Capability fence as terminalization. */ + readonly actorSubjectId: string; readonly permissionScopes: readonly string[]; } @@ -1927,6 +2006,7 @@ async function appendSourceWorkflowActivity( action: "source.failed" | "source.synced", result: "failure" | "success", now: string, + actorSubjectId?: string, ) { if (!run.sourceId) return; const source = await tx.execute({ @@ -1945,12 +2025,13 @@ async function appendSourceWorkflowActivity( const requiredPermissionScope = candidatePermissionScopeSnapshot( jsonStringArrayColumn(row, "permission_scope"), ); + const memberActorId = actorSubjectId ?? run.requestedBySubjectId; await appendKnowledgeSpaceActivityWithExecutor({ database, executor: tx, input: { action, - actor: { id: run.requestedBySubjectId, type: "member" }, + actor: memberActorId ? { id: memberActorId, type: "member" } : { type: "system" }, details: { ...(run.lastErrorCode ? { reasonCode: run.lastErrorCode } : {}), count: run.progressCompleted, diff --git a/knowledge-fs/packages/api/src/source-product-workflow-runtime.test.ts b/knowledge-fs/packages/api/src/source-product-workflow-runtime.test.ts index c86738057b8..0a0f3b86076 100644 --- a/knowledge-fs/packages/api/src/source-product-workflow-runtime.test.ts +++ b/knowledge-fs/packages/api/src/source-product-workflow-runtime.test.ts @@ -174,7 +174,28 @@ describe("source-product workflow staged content store", () => { describe("source-product workflow provider imports", () => { it("stages a crawl preview, imports the frozen selection, and drains staged content", async () => { - const source = sourceRecord("crawl-preview-source", { type: "web" }); + let source = sourceRecord("crawl-preview-source", { + metadata: { preview: true }, + status: "disabled", + type: "web", + }); + const sources = { + get: vi.fn(async () => source), + update: vi.fn( + async (input: { + readonly metadata?: Source["metadata"]; + readonly status?: Source["status"]; + }) => { + source = { + ...source, + ...(input.metadata ? { metadata: input.metadata } : {}), + ...(input.status ? { status: input.status } : {}), + version: source.version + 1, + }; + return source; + }, + ), + }; const pages = [ { content: "First page body", @@ -219,6 +240,7 @@ describe("source-product workflow provider imports", () => { maxCleanupBatchesPerRun: 2, run, source, + sources: sources as never, websiteCrawl: { crawl: vi.fn(async () => ({ pages })) }, }); @@ -255,6 +277,7 @@ describe("source-product workflow provider imports", () => { }); expect(fixture.publish).toHaveBeenCalledTimes(2); expect(deleteRun).toHaveBeenCalledTimes(2); + expect(source).toMatchObject({ metadata: { preview: false }, status: "active", version: 2 }); }); it("imports online-document records with and without optional identity metadata", async () => { diff --git a/knowledge-fs/packages/api/src/source-product-workflow-runtime.ts b/knowledge-fs/packages/api/src/source-product-workflow-runtime.ts index 10e9f51e9a8..188d21671de 100644 --- a/knowledge-fs/packages/api/src/source-product-workflow-runtime.ts +++ b/knowledge-fs/packages/api/src/source-product-workflow-runtime.ts @@ -271,6 +271,7 @@ export function createSourceProductWorkflowRuntime(input: { ) { await cleanupStagedContent(input, execution, maxCleanupBatchesPerRun); } + await activateImportedPreviewSource(input, execution, source); await execution.assertActive(); await input.repository.complete({ fence: fence(execution.run()), @@ -597,6 +598,32 @@ async function cleanupStagedContent( ); } +async function activateImportedPreviewSource( + input: Parameters[0], + execution: RuntimeExecution, + source: Source | null, +): Promise { + const run = execution.run(); + if ( + !source || + run.kind !== "crawl-preview" || + selectedPageIds(run).length === 0 || + source.status !== "disabled" || + source.metadata.preview !== true + ) { + return; + } + await execution.assertActive(); + const activated = await input.sources.update({ + expectedVersion: source.version, + id: source.id, + knowledgeSpaceId: run.knowledgeSpaceId, + metadata: { ...source.metadata, preview: false }, + status: "active", + }); + if (!activated) throw runtimeError("SOURCE_NOT_FOUND", "Source no longer exists"); +} + async function processCrawlPreview( input: Parameters[0], execution: RuntimeExecution, diff --git a/knowledge-fs/packages/generation/src/dify-model-runtime-llm.test.ts b/knowledge-fs/packages/generation/src/dify-model-runtime-llm.test.ts index c87cb80ef19..c512f9e0a38 100644 --- a/knowledge-fs/packages/generation/src/dify-model-runtime-llm.test.ts +++ b/knowledge-fs/packages/generation/src/dify-model-runtime-llm.test.ts @@ -116,6 +116,30 @@ describe("Dify model runtime LLM provider", () => { }); }); + it("keeps content from Dify stream frames with null usage", async () => { + const provider = createDifyModelRuntimeLlmProvider({ + ...BASE, + client: fakeClient(() => [ + { + delta: { + finish_reason: null, + message: { content: "Reply OK." }, + usage: null, + }, + model: BASE.model, + }, + ]), + }); + + const result = await provider.generate({ + messages: [{ content: "Reply OK.", role: "user" }], + model: BASE.model, + tenantId: "tenant-abc", + }); + + expect(result.text).toBe("Reply OK."); + }); + it("requires a per-call tenantId and validates constructor options", async () => { const provider = createDifyModelRuntimeLlmProvider({ ...BASE, diff --git a/knowledge-fs/packages/generation/src/index.ts b/knowledge-fs/packages/generation/src/index.ts index 662891061a5..1e6a1e142df 100644 --- a/knowledge-fs/packages/generation/src/index.ts +++ b/knowledge-fs/packages/generation/src/index.ts @@ -745,7 +745,7 @@ const DifyModelRuntimeLlmChunkSchema = z.object({ total_tokens: z.number(), }) .partial() - .optional(), + .nullish(), }) .partial() .optional(), diff --git a/knowledge-fs/scripts/export-capability-v2-operations.test.mjs b/knowledge-fs/scripts/export-capability-v2-operations.test.mjs index bf8c14b5ed3..735262bffa4 100644 --- a/knowledge-fs/scripts/export-capability-v2-operations.test.mjs +++ b/knowledge-fs/scripts/export-capability-v2-operations.test.mjs @@ -20,7 +20,46 @@ test("Capability v2 operation export is deterministic and includes internal life ); const document = JSON.parse(readFileSync(output, "utf8")); assert.equal(document.schemaVersion, 1); - assert.equal(new Set(document.operations.map((operation) => operation.operationId)).size, 63); + assert.equal(new Set(document.operations.map((operation) => operation.operationId)).size, 78); + assert.deepEqual( + document.operations.find((operation) => operation.operationId === "createSourceSyncWorkflow"), + { + action: "source_workflows.sync.create", + allowedCallerKinds: ["interactive", "service", "agent", "workflow"], + method: "POST", + operationId: "createSourceSyncWorkflow", + parentResourceBinding: { pathParameter: "id" }, + path: "/knowledge-spaces/{id}/sources/{sourceId}/sync", + resourceBinding: { pathParameter: "sourceId" }, + resourceType: "source", + }, + ); + assert.deepEqual( + document.operations.find((operation) => operation.operationId === "listSourceProviders"), + { + action: "source_providers.list", + allowedCallerKinds: ["interactive", "service", "agent", "workflow"], + method: "GET", + operationId: "listSourceProviders", + parentResourceBinding: null, + path: "/source-providers", + resourceBinding: { namespace: true }, + resourceType: "namespace", + }, + ); + assert.deepEqual( + document.operations.find((operation) => operation.operationId === "getSourceWorkflow"), + { + action: "source_workflows.read", + allowedCallerKinds: ["interactive", "service", "agent", "workflow"], + method: "GET", + operationId: "getSourceWorkflow", + parentResourceBinding: { pathParameter: "id" }, + path: "/knowledge-spaces/{id}/source-workflows/{runId}", + resourceBinding: { pathParameter: "runId" }, + resourceType: "job", + }, + ); assert.deepEqual( document.operations.find((operation) => operation.operationId === "cancelBackgroundTask"), { diff --git a/knowledge-fs/scripts/export-openapi.mjs b/knowledge-fs/scripts/export-openapi.mjs index cbb58bcdce4..a9a4d1d4735 100644 --- a/knowledge-fs/scripts/export-openapi.mjs +++ b/knowledge-fs/scripts/export-openapi.mjs @@ -16,7 +16,7 @@ function argumentValue(name) { process.env.NODE_ENV = "test"; const [ { createNodePlatformAdapter }, - { createKnowledgeGateway }, + { createKnowledgeGateway, registerSourceProductHandlers }, { createInMemoryCapabilityGrantProvenanceRepository }, ] = await Promise.all([ import("../packages/adapters/src/node.ts"), @@ -53,6 +53,20 @@ const app = createKnowledgeGateway({ putSmallFile: unavailableInContractExport, }, }); +const unavailableService = new Proxy( + {}, + { + get: () => unavailableInContractExport, + }, +); +registerSourceProductHandlers({ + app, + authorization: unavailableService, + connections: unavailableService, + providers: unavailableService, + repository: unavailableService, + workflows: unavailableService, +}); const response = await app.request("/openapi.json"); if (!response.ok) { throw new Error(`OpenAPI export failed with HTTP ${response.status}`); diff --git a/knowledge-fs/scripts/export-openapi.test.mjs b/knowledge-fs/scripts/export-openapi.test.mjs index bb5de5bcfa1..e4ad635d08a 100644 --- a/knowledge-fs/scripts/export-openapi.test.mjs +++ b/knowledge-fs/scripts/export-openapi.test.mjs @@ -54,6 +54,23 @@ test("OpenAPI export is hermetic when invoked from a production environment", () document.paths["/upload-sessions/{id}/abort"].post.operationId, "abortUploadSession", ); + assert.equal( + document.paths["/knowledge-spaces/{id}/sources/{sourceId}/sync"].post.operationId, + "createSourceSyncWorkflow", + ); + assert.equal(document.paths["/source-providers"].get.operationId, "listSourceProviders"); + assert.equal( + document.paths["/knowledge-spaces/{id}/source-connections"].post.operationId, + "createSourceConnection", + ); + assert.equal( + document.paths["/knowledge-spaces/{id}/sources/{sourceId}/crawl-preview"].post.operationId, + "createSourceCrawlPreviewWorkflow", + ); + assert.equal( + document.paths["/knowledge-spaces/{id}/source-workflows/{runId}/selection"].post.operationId, + "selectCrawlPreviewPages", + ); for (const legacyPath of [ "/knowledge-spaces/{id}/access-policy", "/knowledge-spaces/{id}/members", diff --git a/packages/contracts/console.ts b/packages/contracts/console.ts index af4c1f89f3e..dcf94149767 100644 --- a/packages/contracts/console.ts +++ b/packages/contracts/console.ts @@ -1,7 +1 @@ -import { consoleRouterContract as generatedConsoleRouterContract } from './generated/api/console/router.gen' -import { contract as knowledgeFsContract } from './generated/knowledge-fs/orpc.gen' - -export const consoleRouterContract = { - ...generatedConsoleRouterContract, - knowledgeFs: knowledgeFsContract, -} +export { consoleRouterContract } from './generated/api/console/router.gen' diff --git a/packages/contracts/generated/api/console/knowledge-fs/orpc.gen.ts b/packages/contracts/generated/api/console/knowledge-fs/orpc.gen.ts index 550778d58ca..2d5dd8c8e46 100644 --- a/packages/contracts/generated/api/console/knowledge-fs/orpc.gen.ts +++ b/packages/contracts/generated/api/console/knowledge-fs/orpc.gen.ts @@ -8,9 +8,11 @@ import { zDeleteKnowledgeFsSpacesByControlSpaceIdCredentialsByCredentialIdPath, zDeleteKnowledgeFsSpacesByControlSpaceIdCredentialsByCredentialIdResponse, zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkBody, + zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkHeaders, zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkPath, zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkResponse, zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdBody, + zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdHeaders, zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdPath, zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdResponse, zDeleteKnowledgeFsSpacesByControlSpaceIdJobsByJobIdPath, @@ -20,11 +22,15 @@ import { zDeleteKnowledgeFsSpacesByControlSpaceIdResearchTasksByTaskIdResponse, zDeleteKnowledgeFsSpacesByControlSpaceIdResponse, zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdBody, + zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdHeaders, zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPath, zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdQuery, zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponse, zGetKnowledgeFsSpacesByControlSpaceIdAppBindingsPath, zGetKnowledgeFsSpacesByControlSpaceIdAppBindingsResponse, + zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksPath, + zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksQuery, + zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksResponse, zGetKnowledgeFsSpacesByControlSpaceIdBulkJobsByJobIdPath, zGetKnowledgeFsSpacesByControlSpaceIdBulkJobsByJobIdResponse, zGetKnowledgeFsSpacesByControlSpaceIdCredentialsPath, @@ -48,6 +54,21 @@ import { zGetKnowledgeFsSpacesByControlSpaceIdExternalAccessResponse, zGetKnowledgeFsSpacesByControlSpaceIdJobsByJobIdPath, zGetKnowledgeFsSpacesByControlSpaceIdJobsByJobIdResponse, + zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdPath, + zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdResponse, + zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsPath, + zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsQuery, + zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsResponse, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewHealthPath, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewHealthResponse, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryPath, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryResponse, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesPath, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesQuery, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesResponse, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsPath, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsQuery, + zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsResponse, zGetKnowledgeFsSpacesByControlSpaceIdPath, zGetKnowledgeFsSpacesByControlSpaceIdPermissionsPath, zGetKnowledgeFsSpacesByControlSpaceIdPermissionsResponse, @@ -62,6 +83,11 @@ import { zGetKnowledgeFsSpacesByControlSpaceIdResponse, zGetKnowledgeFsSpacesByControlSpaceIdSettingsPath, zGetKnowledgeFsSpacesByControlSpaceIdSettingsResponse, + zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsPath, + zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsQuery, + zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponse, + zGetKnowledgeFsSpacesByControlSpaceIdSourceProvidersPath, + zGetKnowledgeFsSpacesByControlSpaceIdSourceProvidersResponse, zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdFilesPath, zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdFilesQuery, zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdFilesResponse, @@ -70,9 +96,16 @@ import { zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPagesResponse, zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPath, zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponse, + zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyPath, + zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponse, zGetKnowledgeFsSpacesByControlSpaceIdSourcesPath, zGetKnowledgeFsSpacesByControlSpaceIdSourcesQuery, zGetKnowledgeFsSpacesByControlSpaceIdSourcesResponse, + zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesPath, + zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesQuery, + zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesResponse, + zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPath, + zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdResponse, zGetKnowledgeFsSpacesByControlSpaceIdTracesByTraceIdConflictsPath, zGetKnowledgeFsSpacesByControlSpaceIdTracesByTraceIdConflictsQuery, zGetKnowledgeFsSpacesByControlSpaceIdTracesByTraceIdConflictsResponse, @@ -103,6 +136,10 @@ import { zPatchKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPath, zPatchKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponse, zPostKnowledgeFsSpacesBody, + zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelPath, + zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelResponse, + zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryPath, + zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryResponse, zPostKnowledgeFsSpacesByControlSpaceIdCredentialsBody, zPostKnowledgeFsSpacesByControlSpaceIdCredentialsPath, zPostKnowledgeFsSpacesByControlSpaceIdCredentialsResponse, @@ -128,19 +165,38 @@ import { zPostKnowledgeFsSpacesByControlSpaceIdResearchTasksPlanPath, zPostKnowledgeFsSpacesByControlSpaceIdResearchTasksPlanResponse, zPostKnowledgeFsSpacesByControlSpaceIdResearchTasksResponse, + zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsBody, + zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshBody, + zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshPath, + zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshResponse, + zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsPath, + zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponse, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBody, - zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPath, - zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlResponse, + zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewHeaders, + zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewPath, + zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewResponse, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdImportBody, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdImportFilesBody, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdImportFilesPath, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdImportFilesResponse, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdImportPath, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdImportResponse, + zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncHeaders, + zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPath, + zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncResponse, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdTestPath, zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdTestResponse, zPostKnowledgeFsSpacesByControlSpaceIdSourcesPath, zPostKnowledgeFsSpacesByControlSpaceIdSourcesResponse, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelBody, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelPath, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelResponse, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryPath, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryResponse, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionBody, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionHeaders, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionPath, + zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionResponse, zPostKnowledgeFsSpacesByControlSpaceIdUploadCapabilitiesBody, zPostKnowledgeFsSpacesByControlSpaceIdUploadCapabilitiesPath, zPostKnowledgeFsSpacesByControlSpaceIdUploadCapabilitiesResponse, @@ -160,6 +216,9 @@ import { zPutKnowledgeFsSpacesByControlSpaceIdMembersBody, zPutKnowledgeFsSpacesByControlSpaceIdMembersPath, zPutKnowledgeFsSpacesByControlSpaceIdMembersResponse, + zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyBody, + zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyPath, + zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponse, } from './zod.gen' export const get = oc @@ -237,7 +296,75 @@ export const appBindings = { byCallerKind, } +export const post = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancel', + path: '/knowledge-fs/spaces/{control_space_id}/background-tasks/{task_kind}/{task_id}/cancel', + tags: ['console'], + }) + .input( + z.object({ + params: zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelPath, + }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelResponse) + +export const cancel = { + post, +} + +export const post2 = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetry', + path: '/knowledge-fs/spaces/{control_space_id}/background-tasks/{task_kind}/{task_id}/retry', + tags: ['console'], + }) + .input( + z.object({ + params: zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryPath, + }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryResponse) + +export const retry = { + post: post2, +} + +export const byTaskId = { + cancel, + retry, +} + +export const byTaskKind = { + byTaskId, +} + export const get3 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdBackgroundTasks', + path: '/knowledge-fs/spaces/{control_space_id}/background-tasks', + tags: ['console'], + }) + .input( + z.object({ + params: zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksPath, + query: zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksQuery.optional(), + }), + ) + .output(zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksResponse) + +export const backgroundTasks = { + get: get3, + byTaskKind, +} + +export const get4 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -249,7 +376,7 @@ export const get3 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdBulkJobsByJobIdResponse) export const byJobId = { - get: get3, + get: get4, } export const bulkJobs = { @@ -274,7 +401,7 @@ export const byCredentialId = { delete: delete2, } -export const get4 = oc +export const get5 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -285,7 +412,7 @@ export const get4 = oc .input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdCredentialsPath })) .output(zGetKnowledgeFsSpacesByControlSpaceIdCredentialsResponse) -export const post = oc +export const post3 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -303,8 +430,8 @@ export const post = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdCredentialsResponse) export const credentials = { - get: get4, - post, + get: get5, + post: post3, byCredentialId, } @@ -320,6 +447,7 @@ export const delete3 = oc .input( z.object({ body: zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkBody, + headers: zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkHeaders, params: zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkPath, }), ) @@ -329,7 +457,7 @@ export const bulk = { delete: delete3, } -export const post2 = oc +export const post4 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -346,10 +474,10 @@ export const post2 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdDocumentsReindexResponse) export const reindex = { - post: post2, + post: post4, } -export const get5 = oc +export const get6 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -363,10 +491,10 @@ export const get5 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdOutlineResponse) export const outline = { - get: get5, + get: get6, } -export const get6 = oc +export const get7 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -386,10 +514,10 @@ export const get6 = oc ) export const byChunkId = { - get: get6, + get: get7, } -export const get7 = oc +export const get8 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -411,7 +539,7 @@ export const get7 = oc ) export const chunks = { - get: get7, + get: get8, byChunkId, } @@ -419,7 +547,7 @@ export const byRevision = { chunks, } -export const get8 = oc +export const get9 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -436,7 +564,7 @@ export const get8 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdRevisionsResponse) export const revisions = { - get: get8, + get: get9, byRevision, } @@ -452,12 +580,13 @@ export const delete4 = oc .input( z.object({ body: zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdBody, + headers: zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdHeaders, params: zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdPath, }), ) .output(zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdResponse) -export const get9 = oc +export const get10 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -486,13 +615,13 @@ export const patch = oc export const byDocumentId = { delete: delete4, - get: get9, + get: get10, patch, outline, revisions, } -export const get10 = oc +export const get11 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -511,7 +640,7 @@ export const get10 = oc /** * @deprecated */ -export const post3 = oc +export const post5 = oc .route({ deprecated: true, inputStructure: 'detailed', @@ -530,14 +659,14 @@ export const post3 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdDocumentsResponse) export const documents = { - get: get10, - post: post3, + get: get11, + post: post5, bulk, reindex, byDocumentId, } -export const get11 = oc +export const get12 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -565,11 +694,11 @@ export const put2 = oc .output(zPutKnowledgeFsSpacesByControlSpaceIdExternalAccessResponse) export const externalAccess = { - get: get11, + get: get12, put: put2, } -export const post4 = oc +export const post6 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -580,8 +709,8 @@ export const post4 = oc .input(z.object({ params: zPostKnowledgeFsSpacesByControlSpaceIdJobsByJobIdRetryPath })) .output(zPostKnowledgeFsSpacesByControlSpaceIdJobsByJobIdRetryResponse) -export const retry = { - post: post4, +export const retry2 = { + post: post6, } export const delete5 = oc @@ -595,7 +724,7 @@ export const delete5 = oc .input(z.object({ params: zDeleteKnowledgeFsSpacesByControlSpaceIdJobsByJobIdPath })) .output(zDeleteKnowledgeFsSpacesByControlSpaceIdJobsByJobIdResponse) -export const get12 = oc +export const get13 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -608,14 +737,52 @@ export const get12 = oc export const byJobId2 = { delete: delete5, - get: get12, - retry, + get: get13, + retry: retry2, } export const jobs = { byJobId: byJobId2, } +export const get14 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentId', + path: '/knowledge-fs/spaces/{control_space_id}/logical-documents/{document_id}', + tags: ['console'], + }) + .input( + z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdPath }), + ) + .output(zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdResponse) + +export const byDocumentId2 = { + get: get14, +} + +export const get15 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdLogicalDocuments', + path: '/knowledge-fs/spaces/{control_space_id}/logical-documents', + tags: ['console'], + }) + .input( + z.object({ + params: zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsPath, + query: zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsQuery.optional(), + }), + ) + .output(zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsResponse) + +export const logicalDocuments = { + get: get15, + byDocumentId: byDocumentId2, +} + export const put3 = oc .route({ inputStructure: 'detailed', @@ -636,7 +803,84 @@ export const members = { put: put3, } -export const get13 = oc +export const get16 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdOverviewHealth', + path: '/knowledge-fs/spaces/{control_space_id}/overview/health', + tags: ['console'], + }) + .input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdOverviewHealthPath })) + .output(zGetKnowledgeFsSpacesByControlSpaceIdOverviewHealthResponse) + +export const health = { + get: get16, +} + +export const get17 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdOverviewInventory', + path: '/knowledge-fs/spaces/{control_space_id}/overview/inventory', + tags: ['console'], + }) + .input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryPath })) + .output(zGetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryResponse) + +export const inventory = { + get: get17, +} + +export const get18 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomes', + path: '/knowledge-fs/spaces/{control_space_id}/overview/query-outcomes', + tags: ['console'], + }) + .input( + z.object({ + params: zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesPath, + query: zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesQuery.optional(), + }), + ) + .output(zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesResponse) + +export const queryOutcomes = { + get: get18, +} + +export const get19 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdOverviewStats', + path: '/knowledge-fs/spaces/{control_space_id}/overview/stats', + tags: ['console'], + }) + .input( + z.object({ + params: zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsPath, + query: zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsQuery.optional(), + }), + ) + .output(zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsResponse) + +export const stats = { + get: get19, +} + +export const overview = { + health, + inventory, + queryOutcomes, + stats, +} + +export const get20 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -648,10 +892,10 @@ export const get13 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdPermissionsResponse) export const permissions = { - get: get13, + get: get20, } -export const post5 = oc +export const post7 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -668,13 +912,13 @@ export const post5 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdQueriesAdmissionResponse) export const admission = { - post: post5, + post: post7, } /** * @deprecated */ -export const post6 = oc +export const post8 = oc .route({ deprecated: true, inputStructure: 'detailed', @@ -693,14 +937,14 @@ export const post6 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdQueriesResponse) export const queries = { - post: post6, + post: post8, admission, } /** * @deprecated */ -export const post7 = oc +export const post9 = oc .route({ deprecated: true, inputStructure: 'detailed', @@ -713,10 +957,10 @@ export const post7 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdQueryStreamCapabilityResponse) export const queryStreamCapability = { - post: post7, + post: post9, } -export const post8 = oc +export const post10 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -733,10 +977,10 @@ export const post8 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdResearchTasksPlanResponse) export const plan = { - post: post8, + post: post10, } -export const get14 = oc +export const get21 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -753,7 +997,7 @@ export const get14 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdResearchTasksByTaskIdPartialsResponse) export const partials = { - get: get14, + get: get21, } export const delete6 = oc @@ -767,7 +1011,7 @@ export const delete6 = oc .input(z.object({ params: zDeleteKnowledgeFsSpacesByControlSpaceIdResearchTasksByTaskIdPath })) .output(zDeleteKnowledgeFsSpacesByControlSpaceIdResearchTasksByTaskIdResponse) -export const get15 = oc +export const get22 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -778,13 +1022,13 @@ export const get15 = oc .input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdResearchTasksByTaskIdPath })) .output(zGetKnowledgeFsSpacesByControlSpaceIdResearchTasksByTaskIdResponse) -export const byTaskId = { +export const byTaskId2 = { delete: delete6, - get: get15, + get: get22, partials, } -export const get16 = oc +export const get23 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -800,7 +1044,7 @@ export const get16 = oc ) .output(zGetKnowledgeFsSpacesByControlSpaceIdResearchTasksResponse) -export const post9 = oc +export const post11 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -818,13 +1062,13 @@ export const post9 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdResearchTasksResponse) export const researchTasks = { - get: get16, - post: post9, + get: get23, + post: post11, plan, - byTaskId, + byTaskId: byTaskId2, } -export const get17 = oc +export const get24 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -852,26 +1096,212 @@ export const patch2 = oc .output(zPatchKnowledgeFsSpacesByControlSpaceIdSettingsResponse) export const settings = { - get: get17, + get: get24, patch: patch2, } -export const post10 = oc +export const post12 = oc .route({ inputStructure: 'detailed', method: 'POST', - operationId: 'postKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawl', - path: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/crawl', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefresh', + path: '/knowledge-fs/spaces/{control_space_id}/source-connections/{connection_id}/refresh', tags: ['console'], }) - .input(z.object({ params: zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPath })) - .output(zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlResponse) + .input( + z.object({ + body: zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshBody, + params: zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshPath, + }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshResponse) -export const crawl = { - post: post10, +export const refresh = { + post: post12, } -export const get18 = oc +export const byConnectionId = { + refresh, +} + +export const get25 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdSourceConnections', + path: '/knowledge-fs/spaces/{control_space_id}/source-connections', + tags: ['console'], + }) + .input( + z.object({ + params: zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsPath, + query: zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsQuery.optional(), + }), + ) + .output(zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponse) + +export const post13 = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdSourceConnections', + path: '/knowledge-fs/spaces/{control_space_id}/source-connections', + successStatus: 201, + tags: ['console'], + }) + .input( + z.object({ + body: zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsBody, + params: zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsPath, + }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponse) + +export const sourceConnections = { + get: get25, + post: post13, + byConnectionId, +} + +export const get26 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdSourceProviders', + path: '/knowledge-fs/spaces/{control_space_id}/source-providers', + tags: ['console'], + }) + .input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdSourceProvidersPath })) + .output(zGetKnowledgeFsSpacesByControlSpaceIdSourceProvidersResponse) + +export const sourceProviders = { + get: get26, +} + +export const post14 = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancel', + path: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}/cancel', + tags: ['console'], + }) + .input( + z.object({ + body: zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelBody, + params: zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelPath, + }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelResponse) + +export const cancel2 = { + post: post14, +} + +export const get27 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPages', + path: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}/pages', + tags: ['console'], + }) + .input( + z.object({ + params: zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesPath, + query: zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesQuery.optional(), + }), + ) + .output(zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesResponse) + +export const pages = { + get: get27, +} + +export const post15 = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetry', + path: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}/retry', + tags: ['console'], + }) + .input( + z.object({ params: zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryPath }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryResponse) + +export const retry3 = { + post: post15, +} + +export const post16 = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelection', + path: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}/selection', + successStatus: 202, + tags: ['console'], + }) + .input( + z.object({ + body: zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionBody, + headers: zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionHeaders, + params: zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionPath, + }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionResponse) + +export const selection = { + post: post16, +} + +export const get28 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunId', + path: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}', + tags: ['console'], + }) + .input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPath })) + .output(zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdResponse) + +export const byRunId = { + get: get28, + cancel: cancel2, + pages, + retry: retry3, + selection, +} + +export const sourceWorkflows = { + byRunId, +} + +export const post17 = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreview', + path: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/crawl-preview', + successStatus: 202, + tags: ['console'], + }) + .input( + z.object({ + headers: zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewHeaders, + params: zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewPath, + }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewResponse) + +export const crawlPreview = { + post: post17, +} + +export const get29 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -888,10 +1318,10 @@ export const get18 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdFilesResponse) export const files = { - get: get18, + get: get29, } -export const post11 = oc +export const post18 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -908,10 +1338,10 @@ export const post11 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdImportResponse) export const import_ = { - post: post11, + post: post18, } -export const post12 = oc +export const post19 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -928,10 +1358,10 @@ export const post12 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdImportFilesResponse) export const importFiles = { - post: post12, + post: post19, } -export const get19 = oc +export const get30 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -947,11 +1377,64 @@ export const get19 = oc ) .output(zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPagesResponse) -export const pages = { - get: get19, +export const pages2 = { + get: get30, } -export const post13 = oc +export const post20 = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSync', + path: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/sync', + successStatus: 202, + tags: ['console'], + }) + .input( + z.object({ + headers: zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncHeaders, + params: zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPath, + }), + ) + .output(zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncResponse) + +export const sync = { + post: post20, +} + +export const get31 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicy', + path: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/sync-policy', + tags: ['console'], + }) + .input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyPath })) + .output(zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponse) + +export const put4 = oc + .route({ + inputStructure: 'detailed', + method: 'PUT', + operationId: 'putKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicy', + path: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/sync-policy', + tags: ['console'], + }) + .input( + z.object({ + body: zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyBody, + params: zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyPath, + }), + ) + .output(zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponse) + +export const syncPolicy = { + get: get31, + put: put4, +} + +export const post21 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -963,7 +1446,7 @@ export const post13 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdTestResponse) export const test = { - post: post13, + post: post21, } export const delete7 = oc @@ -978,13 +1461,14 @@ export const delete7 = oc .input( z.object({ body: zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdBody, + headers: zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdHeaders, params: zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPath, query: zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdQuery.optional(), }), ) .output(zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponse) -export const get20 = oc +export const get32 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1013,17 +1497,19 @@ export const patch3 = oc export const bySourceId = { delete: delete7, - get: get20, + get: get32, patch: patch3, - crawl, + crawlPreview, files, import: import_, importFiles, - pages, + pages: pages2, + sync, + syncPolicy, test, } -export const get21 = oc +export const get33 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1039,7 +1525,7 @@ export const get21 = oc ) .output(zGetKnowledgeFsSpacesByControlSpaceIdSourcesResponse) -export const post14 = oc +export const post22 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1057,12 +1543,12 @@ export const post14 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdSourcesResponse) export const sources = { - get: get21, - post: post14, + get: get33, + post: post22, bySourceId, } -export const get22 = oc +export const get34 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1079,10 +1565,10 @@ export const get22 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdTracesByTraceIdConflictsResponse) export const conflicts = { - get: get22, + get: get34, } -export const get23 = oc +export const get35 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1099,10 +1585,10 @@ export const get23 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdTracesByTraceIdEvidenceResponse) export const evidence = { - get: get23, + get: get35, } -export const get24 = oc +export const get36 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1119,10 +1605,10 @@ export const get24 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdTracesByTraceIdMissingResponse) export const missing = { - get: get24, + get: get36, } -export const get25 = oc +export const get37 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1134,13 +1620,13 @@ export const get25 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdTracesByTraceIdResponse) export const byTraceId = { - get: get25, + get: get37, conflicts, evidence, missing, } -export const get26 = oc +export const get38 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1157,11 +1643,11 @@ export const get26 = oc .output(zGetKnowledgeFsSpacesByControlSpaceIdTracesResponse) export const traces = { - get: get26, + get: get38, byTraceId, } -export const post15 = oc +export const post23 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1178,10 +1664,10 @@ export const post15 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdUploadCapabilitiesResponse) export const uploadCapabilities = { - post: post15, + post: post23, } -export const post16 = oc +export const post24 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1198,7 +1684,7 @@ export const post16 = oc .output(zPostKnowledgeFsSpacesByControlSpaceIdUploadSessionsByUploadSessionIdSmallFileResponse) export const smallFile = { - post: post16, + post: post24, } export const byUploadSessionId = { @@ -1221,7 +1707,7 @@ export const delete8 = oc .input(z.object({ params: zDeleteKnowledgeFsSpacesByControlSpaceIdPath })) .output(zDeleteKnowledgeFsSpacesByControlSpaceIdResponse) -export const get27 = oc +export const get39 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1250,27 +1736,33 @@ export const patch4 = oc export const byControlSpaceId = { delete: delete8, - get: get27, + get: get39, patch: patch4, appBindings, + backgroundTasks, bulkJobs, credentials, documents, externalAccess, jobs, + logicalDocuments, members, + overview, permissions, queries, queryStreamCapability, researchTasks, settings, + sourceConnections, + sourceProviders, + sourceWorkflows, sources, traces, uploadCapabilities, uploadSessions, } -export const get28 = oc +export const get40 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -1281,7 +1773,7 @@ export const get28 = oc .input(z.object({ query: zGetKnowledgeFsSpacesQuery.optional() })) .output(zGetKnowledgeFsSpacesResponse) -export const post17 = oc +export const post25 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1294,12 +1786,12 @@ export const post17 = oc .output(zPostKnowledgeFsSpacesResponse) export const spaces = { - get: get28, - post: post17, + get: get40, + post: post25, byControlSpaceId, } -export const post18 = oc +export const post26 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -1316,15 +1808,15 @@ export const post18 = oc .output(zPostKnowledgeFsTasksByTaskIdStreamCapabilityResponse) export const streamCapability = { - post: post18, + post: post26, } -export const byTaskId2 = { +export const byTaskId3 = { streamCapability, } export const tasks = { - byTaskId: byTaskId2, + byTaskId: byTaskId3, } export const knowledgeFs = { diff --git a/packages/contracts/generated/api/console/knowledge-fs/types.gen.ts b/packages/contracts/generated/api/console/knowledge-fs/types.gen.ts index 49850bb877a..22b1afcc89b 100644 --- a/packages/contracts/generated/api/console/knowledge-fs/types.gen.ts +++ b/packages/contracts/generated/api/console/knowledge-fs/types.gen.ts @@ -70,14 +70,52 @@ export type KnowledgeFsAppBindingResponse = { status: KnowledgeFsAppSpaceJoinStatus } +export type KnowledgeFsBackgroundTaskListResponse = { + data: Array + next_cursor?: string | null +} + +export type KnowledgeFsBackgroundTaskResponse = { + can_cancel: boolean + can_retry: boolean + completed_at?: string | null + created_at: string + document_id?: string | null + document_revision?: number | null + error_code?: string | null + error_message?: string | null + id: string + knowledge_space_id: string + operation: + | 'document_delete' + | 'document_processing' + | 'document_reindex' + | 'document_upload' + | 'source_bulk' + | 'source_crawl_import' + | 'source_crawl_preview' + | 'source_online_document_import' + | 'source_online_drive_import' + | 'source_sync' + progress_completed: number + progress_failed: number + progress_percent: number + progress_total: number + source_id?: string | null + state: 'canceled' | 'completed' | 'failed' | 'queued' | 'running' + task_kind: 'document' | 'document_bulk' | 'source' + updated_at: string +} + export type KnowledgeFsBulkJobResponse = { + canceled_items: number completed_items: number created_at: string failed_item_ids: Array failed_items: number id: string knowledge_space_id: string - status: 'completed' | 'failed' | 'running' + status: 'canceled' | 'completed' | 'failed' | 'running' total_items: number type: 'document_delete' | 'document_reindex' | 'document_upload' updated_at: string @@ -268,6 +306,11 @@ export type KnowledgeFsDocumentCompilationJobResponse = { version: number } +export type KnowledgeFsLogicalDocumentListResponse = { + data: Array + next_cursor?: string | null +} + export type KnowledgeFsMembersReplacePayload = { members: Array } @@ -276,6 +319,48 @@ export type KnowledgeFsPermissionListResponse = { data: Array } +export type KnowledgeFsOverviewHealthResponse = { + components: KnowledgeFsOverviewHealthComponentsResponse + generated_at: string + knowledge_space_id: string + state: 'degraded' | 'healthy' | 'unavailable' | 'unknown' +} + +export type KnowledgeFsOverviewInventoryResponse = { + generated_at: string + graph_entities: KnowledgeFsOverviewInventoryDeltaResponse + graph_relations: KnowledgeFsOverviewInventoryDeltaResponse + index_coverage: KnowledgeFsOverviewIndexCoverageResponse + knowledge_space_id: string + source_categories: KnowledgeFsOverviewSourceCategoriesResponse +} + +export type KnowledgeFsOverviewQueryOutcomesResponse = { + buckets: Array + current: KnowledgeFsOverviewQueryOutcomeCountsResponse + generated_at: string + knowledge_space_id: string + previous: KnowledgeFsOverviewQueryOutcomeCountsResponse + previous_since: string + since: string + window: '24h' | '30d' | '7d' +} + +export type KnowledgeFsOverviewStatsResponse = { + answer_rate: KnowledgeFsOverviewRateComparisonResponse + documents: number + fresh_source_count: number + freshness_seconds?: number | null + generated_at: string + knowledge_space_id: string + latest_source_sync_at?: string | null + linked_apps: number + queries: KnowledgeFsOverviewCountComparisonResponse + source_count: number + stale_source_count: number + window: '24h' | '30d' | '7d' +} + export type KnowledgeFsQueryCreatePayload = { activeDocumentIds?: Array activeEntityIds?: Array @@ -391,6 +476,83 @@ export type KnowledgeFsSettingsPayload = { retrieval?: KnowledgeFsProductRetrievalProfile | null } +export type KnowledgeFsSourceConnectionListResponse = { + data: Array + next_cursor?: string | null +} + +export type KnowledgeFsSourceConnectionCreatePayload = { + authKind: 'api-key' | 'endpoint' + configuration?: { + [key: string]: boolean | number | string + } + credentials: { + [key: string]: unknown + } + name: string + providerId: string +} + +export type KnowledgeFsSourceConnectionResponse = { + auth_kind: 'api-key' | 'endpoint' | 'oauth2' + configuration: { + [key: string]: boolean | number | string + } + created_at: string + error_code?: string | null + expires_at?: string | null + id: string + knowledge_space_id: string + name: string + provider_id: string + scopes: Array + status: 'active' | 'error' | 'expired' | 'provisioning' | 'revoked' + updated_at: string + version: number +} + +export type KnowledgeFsSourceConnectionRefreshPayload = { + expectedVersion: number +} + +export type KnowledgeFsSourceProviderListResponse = { + data: Array +} + +export type KnowledgeFsSourceWorkflowResponse = { + canceled_at?: string | null + checkpoint: string + completed_at?: string | null + created_at: string + cursor?: string | null + execution_attempts: number + id: string + kind: string + knowledge_space_id: string + last_error_code?: string | null + max_execution_attempts: number + progress_completed: number + progress_failed: number + progress_skipped: number + progress_total?: number | null + source_id?: string | null + state: string + updated_at: string +} + +export type KnowledgeFsSourceWorkflowCancelPayload = { + reason?: string | null +} + +export type KnowledgeFsCrawlPreviewPageListResponse = { + data: Array + next_cursor?: string | null +} + +export type KnowledgeFsCrawlPreviewSelectionPayload = { + pageIds: Array +} + export type KnowledgeFsSourceListResponse = { data: Array next_cursor?: string | null @@ -442,17 +604,6 @@ export type KnowledgeFsSourceUpdatePayload = { status?: 'active' | 'disabled' | 'error' | 'syncing' | null } -export type KnowledgeFsSourceCrawlResponse = { - completed?: number | null - failed?: number | null - imported?: number | null - pages: Array - replaced?: number | null - skipped?: number | null - status?: string | null - total?: number | null -} - export type KnowledgeFsSourceFilesResponse = { buckets: Array } @@ -476,6 +627,28 @@ export type KnowledgeFsSourcePagesResponse = { workspaces: Array } +export type KnowledgeFsSourceSyncPolicyResponse = { + created_at: string + custom_interval_seconds?: number | null + enabled: boolean + expected_source_version: number + id: string + knowledge_space_id: string + mode: 'custom' | 'interval' | 'manual' | 'provider' + next_run_at?: string | null + revision: number + source_id: string + updated_at: string +} + +export type KnowledgeFsSourceSyncPolicyPayload = { + customIntervalSeconds?: number | null + enabled: boolean + expectedRevision: number + expectedSourceVersion: number + mode: 'custom' | 'interval' | 'manual' | 'provider' +} + export type KnowledgeFsSourceCredentialTestResponse = { code?: string | null error?: string | null @@ -552,6 +725,7 @@ export type KnowledgeFsjwkResponse = { export type KnowledgeFsSpaceListItemResponse = { control_space_id: string + created_at: string knowledge_space_id: string | null owner_account_id: string permission_keys: Array @@ -559,6 +733,7 @@ export type KnowledgeFsSpaceListItemResponse = { state: KnowledgeFsControlSpaceState technical_status: 'available' | 'not_ready' | 'unavailable' technical_summary?: KnowledgeFsTechnicalSummary | null + updated_at: string visibility: KnowledgeFsControlSpaceVisibility } @@ -727,6 +902,62 @@ export type KnowledgeFsPermissionResponse = { status: string } +export type KnowledgeFsOverviewHealthComponentsResponse = { + index: KnowledgeFsOverviewHealthComponentResponse + ingestion: KnowledgeFsOverviewHealthComponentResponse + profile_publication: KnowledgeFsOverviewHealthComponentResponse + query_availability: KnowledgeFsOverviewHealthComponentResponse + source_freshness: KnowledgeFsOverviewHealthComponentResponse + worker_readiness: KnowledgeFsOverviewHealthComponentResponse +} + +export type KnowledgeFsOverviewInventoryDeltaResponse = { + added_last_7d: number + total: number +} + +export type KnowledgeFsOverviewIndexCoverageResponse = { + indexed: number + percentage: number + total: number +} + +export type KnowledgeFsOverviewSourceCategoriesResponse = { + crawl: number + online_documents: number + online_drives: number + uploads: number +} + +export type KnowledgeFsOverviewQueryOutcomeBucketResponse = { + answered: number + end_at: string + low_confidence: number + no_evidence: number + query_count: number + start_at: string +} + +export type KnowledgeFsOverviewQueryOutcomeCountsResponse = { + answer_rate: number + answered: number + low_confidence: number + no_evidence: number + query_count: number +} + +export type KnowledgeFsOverviewRateComparisonResponse = { + change_percentage_points: number + previous_value: number + value: number +} + +export type KnowledgeFsOverviewCountComparisonResponse = { + change_rate: number | null + previous_value: number + value: number +} + export type KnowledgeFsAdmittedQueryRequest = { activeDocumentIds?: Array activeEntityIds?: Array @@ -802,9 +1033,20 @@ export type KnowledgeFsProductRetrievalProfile = { topK: number } -export type KnowledgeFsCrawledPageResponse = { - content: string +export type KnowledgeFsSourceProviderResponse = { + auth_kinds: Array<'api-key' | 'endpoint' | 'oauth2'> + available: boolean + capabilities: Array<'online-document' | 'online-drive' | 'website-crawl'> + configuration: Array + display_name: string + id: string + unavailable_reason?: string | null +} + +export type KnowledgeFsCrawlPreviewPageResponse = { description?: string | null + etag?: string | null + page_id: string source_url: string title?: string | null } @@ -931,6 +1173,11 @@ export type KnowledgeFsDurableDeletionProgressResponse = { export type KnowledgeFsControlSpacePermissionRole = 'editor' | 'owner' | 'viewer' +export type KnowledgeFsOverviewHealthComponentResponse = { + codes: Array + state: 'degraded' | 'healthy' | 'unavailable' | 'unknown' +} + export type KnowledgeFsProductRerankProfile = { enabled: boolean model?: KnowledgeFsProfileModelSelection | null @@ -942,6 +1189,15 @@ export type KnowledgeFsProductScoreThreshold = { value?: number | null } +export type KnowledgeFsSourceProviderFieldResponse = { + description?: string | null + format?: 'password' | 'uri' | null + name: string + required: boolean + secret: boolean + type: 'boolean' | 'integer' | 'string' +} + export type KnowledgeFsSourceFileResponse = { id: string name: string @@ -1122,6 +1378,62 @@ export type DeleteKnowledgeFsSpacesByControlSpaceIdAppBindingsByCallerKindByAppI export type DeleteKnowledgeFsSpacesByControlSpaceIdAppBindingsByCallerKindByAppIdResponse = DeleteKnowledgeFsSpacesByControlSpaceIdAppBindingsByCallerKindByAppIdResponses[keyof DeleteKnowledgeFsSpacesByControlSpaceIdAppBindingsByCallerKindByAppIdResponses] +export type GetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksData = { + body?: never + path: { + control_space_id: string + } + query?: { + cursor?: string + limit?: number + } + url: '/knowledge-fs/spaces/{control_space_id}/background-tasks' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksResponses = { + 200: KnowledgeFsBackgroundTaskListResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksResponse = + GetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksResponses] + +export type PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelData = { + body?: never + path: { + control_space_id: string + task_id: string + task_kind: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/background-tasks/{task_kind}/{task_id}/cancel' +} + +export type PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelResponses = + { + 200: KnowledgeFsBackgroundTaskResponse + } + +export type PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelResponse = + PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelResponses] + +export type PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryData = { + body?: never + path: { + control_space_id: string + task_id: string + task_kind: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/background-tasks/{task_kind}/{task_id}/retry' +} + +export type PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryResponses = { + 200: KnowledgeFsBackgroundTaskResponse +} + +export type PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryResponse = + PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryResponses] + export type GetKnowledgeFsSpacesByControlSpaceIdBulkJobsByJobIdData = { body?: never path: { @@ -1224,6 +1536,9 @@ export type PostKnowledgeFsSpacesByControlSpaceIdDocumentsResponse = export type DeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkData = { body: KnowledgeFsBulkDocumentDeletePayload + headers: { + 'Idempotency-Key': string + } path: { control_space_id: string } @@ -1256,6 +1571,9 @@ export type PostKnowledgeFsSpacesByControlSpaceIdDocumentsReindexResponse = export type DeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdData = { body: KnowledgeFsDocumentDeletePayload + headers: { + 'Idempotency-Key': string + } path: { control_space_id: string document_id: string @@ -1468,6 +1786,41 @@ export type PostKnowledgeFsSpacesByControlSpaceIdJobsByJobIdRetryResponses = { export type PostKnowledgeFsSpacesByControlSpaceIdJobsByJobIdRetryResponse = PostKnowledgeFsSpacesByControlSpaceIdJobsByJobIdRetryResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdJobsByJobIdRetryResponses] +export type GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsData = { + body?: never + path: { + control_space_id: string + } + query?: { + cursor?: string + } + url: '/knowledge-fs/spaces/{control_space_id}/logical-documents' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsResponses = { + 200: KnowledgeFsLogicalDocumentListResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsResponse = + GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsResponses] + +export type GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdData = { + body?: never + path: { + control_space_id: string + document_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/logical-documents/{document_id}' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdResponses = { + 200: KnowledgeFsLogicalDocumentResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdResponse = + GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdResponses] + export type PutKnowledgeFsSpacesByControlSpaceIdMembersData = { body: KnowledgeFsMembersReplacePayload path: { @@ -1484,6 +1837,74 @@ export type PutKnowledgeFsSpacesByControlSpaceIdMembersResponses = { export type PutKnowledgeFsSpacesByControlSpaceIdMembersResponse = PutKnowledgeFsSpacesByControlSpaceIdMembersResponses[keyof PutKnowledgeFsSpacesByControlSpaceIdMembersResponses] +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewHealthData = { + body?: never + path: { + control_space_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/overview/health' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewHealthResponses = { + 200: KnowledgeFsOverviewHealthResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewHealthResponse = + GetKnowledgeFsSpacesByControlSpaceIdOverviewHealthResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdOverviewHealthResponses] + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryData = { + body?: never + path: { + control_space_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/overview/inventory' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryResponses = { + 200: KnowledgeFsOverviewInventoryResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryResponse = + GetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryResponses] + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesData = { + body?: never + path: { + control_space_id: string + } + query?: { + window?: '24h' | '30d' | '7d' + } + url: '/knowledge-fs/spaces/{control_space_id}/overview/query-outcomes' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesResponses = { + 200: KnowledgeFsOverviewQueryOutcomesResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesResponse = + GetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesResponses] + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewStatsData = { + body?: never + path: { + control_space_id: string + } + query?: { + window?: '24h' | '30d' | '7d' + } + url: '/knowledge-fs/spaces/{control_space_id}/overview/stats' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewStatsResponses = { + 200: KnowledgeFsOverviewStatsResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdOverviewStatsResponse = + GetKnowledgeFsSpacesByControlSpaceIdOverviewStatsResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdOverviewStatsResponses] + export type GetKnowledgeFsSpacesByControlSpaceIdPermissionsData = { body?: never path: { @@ -1684,6 +2105,165 @@ export type PatchKnowledgeFsSpacesByControlSpaceIdSettingsResponses = { export type PatchKnowledgeFsSpacesByControlSpaceIdSettingsResponse = PatchKnowledgeFsSpacesByControlSpaceIdSettingsResponses[keyof PatchKnowledgeFsSpacesByControlSpaceIdSettingsResponses] +export type GetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsData = { + body?: never + path: { + control_space_id: string + } + query?: { + cursor?: string + limit?: number + } + url: '/knowledge-fs/spaces/{control_space_id}/source-connections' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponses = { + 200: KnowledgeFsSourceConnectionListResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponse = + GetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponses] + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsData = { + body: KnowledgeFsSourceConnectionCreatePayload + path: { + control_space_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/source-connections' +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponses = { + 201: KnowledgeFsSourceConnectionResponse +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponse = + PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponses] + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshData = { + body: KnowledgeFsSourceConnectionRefreshPayload + path: { + connection_id: string + control_space_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/source-connections/{connection_id}/refresh' +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshResponses = { + 200: KnowledgeFsSourceConnectionResponse +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshResponse = + PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshResponses] + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceProvidersData = { + body?: never + path: { + control_space_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/source-providers' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceProvidersResponses = { + 200: KnowledgeFsSourceProviderListResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceProvidersResponse = + GetKnowledgeFsSpacesByControlSpaceIdSourceProvidersResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdSourceProvidersResponses] + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdData = { + body?: never + path: { + control_space_id: string + run_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdResponses = { + 200: KnowledgeFsSourceWorkflowResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdResponse = + GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdResponses] + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelData = { + body: KnowledgeFsSourceWorkflowCancelPayload + path: { + control_space_id: string + run_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}/cancel' +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelResponses = { + 200: KnowledgeFsSourceWorkflowResponse +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelResponse = + PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelResponses] + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesData = { + body?: never + path: { + control_space_id: string + run_id: string + } + query?: { + cursor?: string + limit?: number + } + url: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}/pages' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesResponses = { + 200: KnowledgeFsCrawlPreviewPageListResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesResponse = + GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesResponses] + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryData = { + body?: never + path: { + control_space_id: string + run_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}/retry' +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryResponses = { + 200: KnowledgeFsSourceWorkflowResponse +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryResponse = + PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryResponses] + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionData = { + body: KnowledgeFsCrawlPreviewSelectionPayload + headers: { + 'Idempotency-Key': string + } + path: { + control_space_id: string + run_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/source-workflows/{run_id}/selection' +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionResponses = { + 202: KnowledgeFsSourceWorkflowResponse +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionResponse = + PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionResponses] + export type GetKnowledgeFsSpacesByControlSpaceIdSourcesData = { body?: never path: { @@ -1720,6 +2300,9 @@ export type PostKnowledgeFsSpacesByControlSpaceIdSourcesResponse = export type DeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdData = { body: KnowledgeFsSourceDeletePayload + headers: { + 'Idempotency-Key': string + } path: { control_space_id: string source_id: string @@ -1771,22 +2354,25 @@ export type PatchKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponses = { export type PatchKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponse = PatchKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponses[keyof PatchKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponses] -export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlData = { +export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewData = { body?: never + headers: { + 'Idempotency-Key': string + } path: { control_space_id: string source_id: string } query?: never - url: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/crawl' + url: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/crawl-preview' } -export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlResponses = { - 200: KnowledgeFsSourceCrawlResponse +export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewResponses = { + 202: KnowledgeFsSourceWorkflowResponse } -export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlResponse = - PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlResponses] +export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewResponse = + PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewResponses] export type GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdFilesData = { body?: never @@ -1864,6 +2450,60 @@ export type GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPagesResponses export type GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPagesResponse = GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPagesResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPagesResponses] +export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncData = { + body?: never + headers: { + 'Idempotency-Key': string + } + path: { + control_space_id: string + source_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/sync' +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncResponses = { + 202: KnowledgeFsSourceWorkflowResponse +} + +export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncResponse = + PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncResponses[keyof PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncResponses] + +export type GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyData = { + body?: never + path: { + control_space_id: string + source_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/sync-policy' +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponses = { + 200: KnowledgeFsSourceSyncPolicyResponse +} + +export type GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponse = + GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponses[keyof GetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponses] + +export type PutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyData = { + body: KnowledgeFsSourceSyncPolicyPayload + path: { + control_space_id: string + source_id: string + } + query?: never + url: '/knowledge-fs/spaces/{control_space_id}/sources/{source_id}/sync-policy' +} + +export type PutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponses = { + 200: KnowledgeFsSourceSyncPolicyResponse +} + +export type PutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponse = + PutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponses[keyof PutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponses] + export type PostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdTestData = { body?: never path: { diff --git a/packages/contracts/generated/api/console/knowledge-fs/zod.gen.ts b/packages/contracts/generated/api/console/knowledge-fs/zod.gen.ts index 8db364bfa8a..b81b02df4ac 100644 --- a/packages/contracts/generated/api/console/knowledge-fs/zod.gen.ts +++ b/packages/contracts/generated/api/console/knowledge-fs/zod.gen.ts @@ -2,17 +2,62 @@ import * as z from 'zod' +/** + * KnowledgeFSBackgroundTaskResponse + */ +export const zKnowledgeFsBackgroundTaskResponse = z.object({ + can_cancel: z.boolean(), + can_retry: z.boolean(), + completed_at: z.iso.datetime().nullish(), + created_at: z.iso.datetime(), + document_id: z.string().nullish(), + document_revision: z.int().gte(1).nullish(), + error_code: z.string().nullish(), + error_message: z.string().nullish(), + id: z.string(), + knowledge_space_id: z.string(), + operation: z.enum([ + 'document_delete', + 'document_processing', + 'document_reindex', + 'document_upload', + 'source_bulk', + 'source_crawl_import', + 'source_crawl_preview', + 'source_online_document_import', + 'source_online_drive_import', + 'source_sync', + ]), + progress_completed: z.int().gte(0), + progress_failed: z.int().gte(0), + progress_percent: z.int().gte(0).lte(100), + progress_total: z.int().gte(0), + source_id: z.string().nullish(), + state: z.enum(['canceled', 'completed', 'failed', 'queued', 'running']), + task_kind: z.enum(['document', 'document_bulk', 'source']), + updated_at: z.iso.datetime(), +}) + +/** + * KnowledgeFSBackgroundTaskListResponse + */ +export const zKnowledgeFsBackgroundTaskListResponse = z.object({ + data: z.array(zKnowledgeFsBackgroundTaskResponse), + next_cursor: z.string().nullish(), +}) + /** * KnowledgeFSBulkJobResponse */ export const zKnowledgeFsBulkJobResponse = z.object({ + canceled_items: z.int().gte(0), completed_items: z.int().gte(0), created_at: z.iso.datetime(), failed_item_ids: z.array(z.string()), failed_items: z.int().gte(0), id: z.string(), knowledge_space_id: z.string(), - status: z.enum(['completed', 'failed', 'running']), + status: z.enum(['canceled', 'completed', 'failed', 'running']), total_items: z.int().gte(0), type: z.enum(['document_delete', 'document_reindex', 'document_upload']), updated_at: z.iso.datetime(), @@ -216,6 +261,89 @@ export const zKnowledgeFsResearchTaskPlanPayload = z.object({ topK: z.int().gte(1).lte(50).nullish(), }) +/** + * KnowledgeFSSourceConnectionCreatePayload + */ +export const zKnowledgeFsSourceConnectionCreatePayload = z.object({ + authKind: z.enum(['api-key', 'endpoint']), + configuration: z.record(z.string(), z.union([z.boolean(), z.int(), z.string()])).optional(), + credentials: z.record(z.string(), z.unknown()), + name: z.string().min(1).max(160), + providerId: z.string().min(1).max(128), +}) + +/** + * KnowledgeFSSourceConnectionResponse + */ +export const zKnowledgeFsSourceConnectionResponse = z.object({ + auth_kind: z.enum(['api-key', 'endpoint', 'oauth2']), + configuration: z.record(z.string(), z.union([z.boolean(), z.int(), z.string()])), + created_at: z.iso.datetime(), + error_code: z.string().nullish(), + expires_at: z.iso.datetime().nullish(), + id: z.string(), + knowledge_space_id: z.string(), + name: z.string(), + provider_id: z.string(), + scopes: z.array(z.string()), + status: z.enum(['active', 'error', 'expired', 'provisioning', 'revoked']), + updated_at: z.iso.datetime(), + version: z.int().gte(1), +}) + +/** + * KnowledgeFSSourceConnectionListResponse + */ +export const zKnowledgeFsSourceConnectionListResponse = z.object({ + data: z.array(zKnowledgeFsSourceConnectionResponse), + next_cursor: z.string().nullish(), +}) + +/** + * KnowledgeFSSourceConnectionRefreshPayload + */ +export const zKnowledgeFsSourceConnectionRefreshPayload = z.object({ + expectedVersion: z.int().gte(1), +}) + +/** + * KnowledgeFSSourceWorkflowResponse + */ +export const zKnowledgeFsSourceWorkflowResponse = z.object({ + canceled_at: z.iso.datetime().nullish(), + checkpoint: z.string(), + completed_at: z.iso.datetime().nullish(), + created_at: z.iso.datetime(), + cursor: z.string().nullish(), + execution_attempts: z.int().gte(0), + id: z.string(), + kind: z.string(), + knowledge_space_id: z.string(), + last_error_code: z.string().nullish(), + max_execution_attempts: z.int().gte(1), + progress_completed: z.int().gte(0), + progress_failed: z.int().gte(0), + progress_skipped: z.int().gte(0), + progress_total: z.int().gte(0).nullish(), + source_id: z.string().nullish(), + state: z.string(), + updated_at: z.iso.datetime(), +}) + +/** + * KnowledgeFSSourceWorkflowCancelPayload + */ +export const zKnowledgeFsSourceWorkflowCancelPayload = z.object({ + reason: z.string().max(1000).nullish(), +}) + +/** + * KnowledgeFSCrawlPreviewSelectionPayload + */ +export const zKnowledgeFsCrawlPreviewSelectionPayload = z.object({ + pageIds: z.array(z.string()).min(1).max(200), +}) + /** * KnowledgeFSSourceCreatePayload */ @@ -274,6 +402,34 @@ export const zKnowledgeFsSourceUpdatePayload = z.object({ status: z.enum(['active', 'disabled', 'error', 'syncing']).nullish(), }) +/** + * KnowledgeFSSourceSyncPolicyResponse + */ +export const zKnowledgeFsSourceSyncPolicyResponse = z.object({ + created_at: z.iso.datetime(), + custom_interval_seconds: z.int().nullish(), + enabled: z.boolean(), + expected_source_version: z.int().gte(1), + id: z.string(), + knowledge_space_id: z.string(), + mode: z.enum(['custom', 'interval', 'manual', 'provider']), + next_run_at: z.iso.datetime().nullish(), + revision: z.int().gte(1), + source_id: z.string(), + updated_at: z.iso.datetime(), +}) + +/** + * KnowledgeFSSourceSyncPolicyPayload + */ +export const zKnowledgeFsSourceSyncPolicyPayload = z.object({ + customIntervalSeconds: z.int().gte(3600).lte(2592000).nullish(), + enabled: z.boolean(), + expectedRevision: z.int().gte(0), + expectedSourceVersion: z.int().gte(1), + mode: z.enum(['custom', 'interval', 'manual', 'provider']), +}) + /** * KnowledgeFSSourceCredentialTestResponse */ @@ -447,6 +603,7 @@ export const zKnowledgeFsSpaceDetailResponse = z.object({ */ export const zKnowledgeFsSpaceListItemResponse = z.object({ control_space_id: z.string(), + created_at: z.iso.datetime(), knowledge_space_id: z.string().nullable(), owner_account_id: z.string(), permission_keys: z.array(zKnowledgeFsProductPermission), @@ -454,6 +611,7 @@ export const zKnowledgeFsSpaceListItemResponse = z.object({ state: zKnowledgeFsControlSpaceState, technical_status: z.enum(['available', 'not_ready', 'unavailable']), technical_summary: zKnowledgeFsTechnicalSummary.nullish(), + updated_at: z.iso.datetime(), visibility: zKnowledgeFsControlSpaceVisibility, }) @@ -603,6 +761,14 @@ export const zKnowledgeFsDocumentRevisionListResponse = z.object({ next_cursor: z.string().nullish(), }) +/** + * KnowledgeFSLogicalDocumentListResponse + */ +export const zKnowledgeFsLogicalDocumentListResponse = z.object({ + data: z.array(zKnowledgeFsLogicalDocumentResponse), + next_cursor: z.string().nullish(), +}) + /** * KnowledgeFSDocumentOutlineNodeResponse */ @@ -642,6 +808,118 @@ export const zKnowledgeFsDocumentOutlineResponse = z.object({ version: z.int().gte(1), }) +/** + * KnowledgeFSOverviewInventoryDeltaResponse + */ +export const zKnowledgeFsOverviewInventoryDeltaResponse = z.object({ + added_last_7d: z.int().gte(0), + total: z.int().gte(0), +}) + +/** + * KnowledgeFSOverviewIndexCoverageResponse + */ +export const zKnowledgeFsOverviewIndexCoverageResponse = z.object({ + indexed: z.int().gte(0), + percentage: z.number().gte(0).lte(100), + total: z.int().gte(0), +}) + +/** + * KnowledgeFSOverviewSourceCategoriesResponse + */ +export const zKnowledgeFsOverviewSourceCategoriesResponse = z.object({ + crawl: z.int().gte(0), + online_documents: z.int().gte(0), + online_drives: z.int().gte(0), + uploads: z.int().gte(0), +}) + +/** + * KnowledgeFSOverviewInventoryResponse + */ +export const zKnowledgeFsOverviewInventoryResponse = z.object({ + generated_at: z.iso.datetime(), + graph_entities: zKnowledgeFsOverviewInventoryDeltaResponse, + graph_relations: zKnowledgeFsOverviewInventoryDeltaResponse, + index_coverage: zKnowledgeFsOverviewIndexCoverageResponse, + knowledge_space_id: z.string(), + source_categories: zKnowledgeFsOverviewSourceCategoriesResponse, +}) + +/** + * KnowledgeFSOverviewQueryOutcomeBucketResponse + */ +export const zKnowledgeFsOverviewQueryOutcomeBucketResponse = z.object({ + answered: z.int().gte(0), + end_at: z.iso.datetime(), + low_confidence: z.int().gte(0), + no_evidence: z.int().gte(0), + query_count: z.int().gte(0), + start_at: z.iso.datetime(), +}) + +/** + * KnowledgeFSOverviewQueryOutcomeCountsResponse + */ +export const zKnowledgeFsOverviewQueryOutcomeCountsResponse = z.object({ + answer_rate: z.number().gte(0).lte(1), + answered: z.int().gte(0), + low_confidence: z.int().gte(0), + no_evidence: z.int().gte(0), + query_count: z.int().gte(0), +}) + +/** + * KnowledgeFSOverviewQueryOutcomesResponse + */ +export const zKnowledgeFsOverviewQueryOutcomesResponse = z.object({ + buckets: z.array(zKnowledgeFsOverviewQueryOutcomeBucketResponse), + current: zKnowledgeFsOverviewQueryOutcomeCountsResponse, + generated_at: z.iso.datetime(), + knowledge_space_id: z.string(), + previous: zKnowledgeFsOverviewQueryOutcomeCountsResponse, + previous_since: z.iso.datetime(), + since: z.iso.datetime(), + window: z.enum(['24h', '30d', '7d']), +}) + +/** + * KnowledgeFSOverviewRateComparisonResponse + */ +export const zKnowledgeFsOverviewRateComparisonResponse = z.object({ + change_percentage_points: z.number(), + previous_value: z.number().gte(0).lte(1), + value: z.number().gte(0).lte(1), +}) + +/** + * KnowledgeFSOverviewCountComparisonResponse + */ +export const zKnowledgeFsOverviewCountComparisonResponse = z.object({ + change_rate: z.number().nullable(), + previous_value: z.int().gte(0), + value: z.int().gte(0), +}) + +/** + * KnowledgeFSOverviewStatsResponse + */ +export const zKnowledgeFsOverviewStatsResponse = z.object({ + answer_rate: zKnowledgeFsOverviewRateComparisonResponse, + documents: z.int().gte(0), + fresh_source_count: z.int().gte(0), + freshness_seconds: z.int().gte(0).nullish(), + generated_at: z.iso.datetime(), + knowledge_space_id: z.string(), + latest_source_sync_at: z.iso.datetime().nullish(), + linked_apps: z.int().gte(0), + queries: zKnowledgeFsOverviewCountComparisonResponse, + source_count: z.int().gte(0), + stale_source_count: z.int().gte(0), + window: z.enum(['24h', '30d', '7d']), +}) + /** * KnowledgeFSAdmittedQueryRequest */ @@ -802,27 +1080,22 @@ export const zKnowledgeFsProfileModelSelection = z.object({ }) /** - * KnowledgeFSCrawledPageResponse + * KnowledgeFSCrawlPreviewPageResponse */ -export const zKnowledgeFsCrawledPageResponse = z.object({ - content: z.string(), +export const zKnowledgeFsCrawlPreviewPageResponse = z.object({ description: z.string().nullish(), + etag: z.string().nullish(), + page_id: z.string(), source_url: z.string(), title: z.string().nullish(), }) /** - * KnowledgeFSSourceCrawlResponse + * KnowledgeFSCrawlPreviewPageListResponse */ -export const zKnowledgeFsSourceCrawlResponse = z.object({ - completed: z.int().gte(0).nullish(), - failed: z.int().gte(0).nullish(), - imported: z.int().gte(0).nullish(), - pages: z.array(zKnowledgeFsCrawledPageResponse), - replaced: z.int().gte(0).nullish(), - skipped: z.int().gte(0).nullish(), - status: z.string().nullish(), - total: z.int().gte(0).nullish(), +export const zKnowledgeFsCrawlPreviewPageListResponse = z.object({ + data: z.array(zKnowledgeFsCrawlPreviewPageResponse), + next_cursor: z.string().nullish(), }) /** @@ -1127,6 +1400,36 @@ export const zKnowledgeFsPermissionListResponse = z.object({ data: z.array(zKnowledgeFsPermissionResponse), }) +/** + * KnowledgeFSOverviewHealthComponentResponse + */ +export const zKnowledgeFsOverviewHealthComponentResponse = z.object({ + codes: z.array(z.string()), + state: z.enum(['degraded', 'healthy', 'unavailable', 'unknown']), +}) + +/** + * KnowledgeFSOverviewHealthComponentsResponse + */ +export const zKnowledgeFsOverviewHealthComponentsResponse = z.object({ + index: zKnowledgeFsOverviewHealthComponentResponse, + ingestion: zKnowledgeFsOverviewHealthComponentResponse, + profile_publication: zKnowledgeFsOverviewHealthComponentResponse, + query_availability: zKnowledgeFsOverviewHealthComponentResponse, + source_freshness: zKnowledgeFsOverviewHealthComponentResponse, + worker_readiness: zKnowledgeFsOverviewHealthComponentResponse, +}) + +/** + * KnowledgeFSOverviewHealthResponse + */ +export const zKnowledgeFsOverviewHealthResponse = z.object({ + components: zKnowledgeFsOverviewHealthComponentsResponse, + generated_at: z.iso.datetime(), + knowledge_space_id: z.string(), + state: z.enum(['degraded', 'healthy', 'unavailable', 'unknown']), +}) + /** * KnowledgeFSProductRerankProfile */ @@ -1191,6 +1494,38 @@ export const zKnowledgeFsSettingsPayload = z.object({ retrieval: zKnowledgeFsProductRetrievalProfile.nullish(), }) +/** + * KnowledgeFSSourceProviderFieldResponse + */ +export const zKnowledgeFsSourceProviderFieldResponse = z.object({ + description: z.string().nullish(), + format: z.enum(['password', 'uri']).nullish(), + name: z.string(), + required: z.boolean(), + secret: z.boolean(), + type: z.enum(['boolean', 'integer', 'string']), +}) + +/** + * KnowledgeFSSourceProviderResponse + */ +export const zKnowledgeFsSourceProviderResponse = z.object({ + auth_kinds: z.array(z.enum(['api-key', 'endpoint', 'oauth2'])), + available: z.boolean(), + capabilities: z.array(z.enum(['online-document', 'online-drive', 'website-crawl'])), + configuration: z.array(zKnowledgeFsSourceProviderFieldResponse), + display_name: z.string(), + id: z.string(), + unavailable_reason: z.string().nullish(), +}) + +/** + * KnowledgeFSSourceProviderListResponse + */ +export const zKnowledgeFsSourceProviderListResponse = z.object({ + data: z.array(zKnowledgeFsSourceProviderResponse), +}) + /** * KnowledgeFSSourceFileResponse */ @@ -1388,6 +1723,47 @@ export const zDeleteKnowledgeFsSpacesByControlSpaceIdAppBindingsByCallerKindByAp export const zDeleteKnowledgeFsSpacesByControlSpaceIdAppBindingsByCallerKindByAppIdResponse = z.void() +export const zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksPath = z.object({ + control_space_id: z.string(), +}) + +export const zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksQuery = z.object({ + cursor: z.string().min(1).max(8192).optional(), + limit: z.int().gte(1).lte(100).optional().default(50), +}) + +/** + * KnowledgeFS background tasks + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdBackgroundTasksResponse = + zKnowledgeFsBackgroundTaskListResponse + +export const zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelPath = + z.object({ + control_space_id: z.string(), + task_id: z.string(), + task_kind: z.string(), + }) + +/** + * KnowledgeFS background task canceled + */ +export const zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdCancelResponse = + zKnowledgeFsBackgroundTaskResponse + +export const zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryPath = + z.object({ + control_space_id: z.string(), + task_id: z.string(), + task_kind: z.string(), + }) + +/** + * KnowledgeFS background task retried + */ +export const zPostKnowledgeFsSpacesByControlSpaceIdBackgroundTasksByTaskKindByTaskIdRetryResponse = + zKnowledgeFsBackgroundTaskResponse + export const zGetKnowledgeFsSpacesByControlSpaceIdBulkJobsByJobIdPath = z.object({ control_space_id: z.string(), job_id: z.string(), @@ -1460,6 +1836,10 @@ export const zPostKnowledgeFsSpacesByControlSpaceIdDocumentsResponse = zKnowledg export const zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkBody = zKnowledgeFsBulkDocumentDeletePayload +export const zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkHeaders = z.object({ + 'Idempotency-Key': z.string(), +}) + export const zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsBulkPath = z.object({ control_space_id: z.string(), }) @@ -1486,6 +1866,10 @@ export const zPostKnowledgeFsSpacesByControlSpaceIdDocumentsReindexResponse = export const zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdBody = zKnowledgeFsDocumentDeletePayload +export const zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdHeaders = z.object({ + 'Idempotency-Key': z.string(), +}) + export const zDeleteKnowledgeFsSpacesByControlSpaceIdDocumentsByDocumentIdPath = z.object({ control_space_id: z.string(), document_id: z.string(), @@ -1637,6 +2021,31 @@ export const zPostKnowledgeFsSpacesByControlSpaceIdJobsByJobIdRetryPath = z.obje export const zPostKnowledgeFsSpacesByControlSpaceIdJobsByJobIdRetryResponse = zKnowledgeFsDocumentCompilationJobResponse +export const zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsPath = z.object({ + control_space_id: z.string(), +}) + +export const zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsQuery = z.object({ + cursor: z.string().min(1).max(1000).optional(), +}) + +/** + * KnowledgeFS logical documents + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsResponse = + zKnowledgeFsLogicalDocumentListResponse + +export const zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdPath = z.object({ + control_space_id: z.string(), + document_id: z.string(), +}) + +/** + * KnowledgeFS logical document + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdLogicalDocumentsByDocumentIdResponse = + zKnowledgeFsLogicalDocumentResponse + export const zPutKnowledgeFsSpacesByControlSpaceIdMembersBody = zKnowledgeFsMembersReplacePayload export const zPutKnowledgeFsSpacesByControlSpaceIdMembersPath = z.object({ @@ -1649,6 +2058,54 @@ export const zPutKnowledgeFsSpacesByControlSpaceIdMembersPath = z.object({ export const zPutKnowledgeFsSpacesByControlSpaceIdMembersResponse = zKnowledgeFsPermissionListResponse +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewHealthPath = z.object({ + control_space_id: z.string(), +}) + +/** + * KnowledgeFS health + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewHealthResponse = + zKnowledgeFsOverviewHealthResponse + +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryPath = z.object({ + control_space_id: z.string(), +}) + +/** + * KnowledgeFS inventory + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewInventoryResponse = + zKnowledgeFsOverviewInventoryResponse + +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesPath = z.object({ + control_space_id: z.string(), +}) + +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesQuery = z.object({ + window: z.enum(['24h', '30d', '7d']).optional().default('24h'), +}) + +/** + * KnowledgeFS query outcomes + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewQueryOutcomesResponse = + zKnowledgeFsOverviewQueryOutcomesResponse + +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsPath = z.object({ + control_space_id: z.string(), +}) + +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsQuery = z.object({ + window: z.enum(['24h', '30d', '7d']).optional().default('24h'), +}) + +/** + * KnowledgeFS Overview statistics + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdOverviewStatsResponse = + zKnowledgeFsOverviewStatsResponse + export const zGetKnowledgeFsSpacesByControlSpaceIdPermissionsPath = z.object({ control_space_id: z.string(), }) @@ -1791,6 +2248,130 @@ export const zPatchKnowledgeFsSpacesByControlSpaceIdSettingsPath = z.object({ */ export const zPatchKnowledgeFsSpacesByControlSpaceIdSettingsResponse = zKnowledgeFsSettingsResponse +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsPath = z.object({ + control_space_id: z.string(), +}) + +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsQuery = z.object({ + cursor: z.string().min(1).max(4096).optional(), + limit: z.int().gte(1).lte(200).optional().default(50), +}) + +/** + * KnowledgeFS source connections + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponse = + zKnowledgeFsSourceConnectionListResponse + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsBody = + zKnowledgeFsSourceConnectionCreatePayload + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsPath = z.object({ + control_space_id: z.string(), +}) + +/** + * KnowledgeFS source connection created + */ +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsResponse = + zKnowledgeFsSourceConnectionResponse + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshBody = + zKnowledgeFsSourceConnectionRefreshPayload + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshPath = + z.object({ + connection_id: z.string(), + control_space_id: z.string(), + }) + +/** + * KnowledgeFS source connection refreshed + */ +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceConnectionsByConnectionIdRefreshResponse = + zKnowledgeFsSourceConnectionResponse + +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceProvidersPath = z.object({ + control_space_id: z.string(), +}) + +/** + * KnowledgeFS source providers + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceProvidersResponse = + zKnowledgeFsSourceProviderListResponse + +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPath = z.object({ + control_space_id: z.string(), + run_id: z.string(), +}) + +/** + * KnowledgeFS source workflow + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdResponse = + zKnowledgeFsSourceWorkflowResponse + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelBody = + zKnowledgeFsSourceWorkflowCancelPayload + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelPath = z.object({ + control_space_id: z.string(), + run_id: z.string(), +}) + +/** + * KnowledgeFS source workflow canceled + */ +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdCancelResponse = + zKnowledgeFsSourceWorkflowResponse + +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesPath = z.object({ + control_space_id: z.string(), + run_id: z.string(), +}) + +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesQuery = z.object({ + cursor: z.string().min(1).max(4096).optional(), + limit: z.int().gte(1).lte(200).optional().default(50), +}) + +/** + * KnowledgeFS crawl preview pages + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdPagesResponse = + zKnowledgeFsCrawlPreviewPageListResponse + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryPath = z.object({ + control_space_id: z.string(), + run_id: z.string(), +}) + +/** + * KnowledgeFS source workflow retried + */ +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdRetryResponse = + zKnowledgeFsSourceWorkflowResponse + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionBody = + zKnowledgeFsCrawlPreviewSelectionPayload + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionHeaders = + z.object({ + 'Idempotency-Key': z.string(), + }) + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionPath = z.object({ + control_space_id: z.string(), + run_id: z.string(), +}) + +/** + * KnowledgeFS crawl preview selection accepted + */ +export const zPostKnowledgeFsSpacesByControlSpaceIdSourceWorkflowsByRunIdSelectionResponse = + zKnowledgeFsSourceWorkflowResponse + export const zGetKnowledgeFsSpacesByControlSpaceIdSourcesPath = z.object({ control_space_id: z.string(), }) @@ -1818,6 +2399,10 @@ export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesResponse = zKnowledgeF export const zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdBody = zKnowledgeFsSourceDeletePayload +export const zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdHeaders = z.object({ + 'Idempotency-Key': z.string(), +}) + export const zDeleteKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPath = z.object({ control_space_id: z.string(), source_id: z.string(), @@ -1858,16 +2443,20 @@ export const zPatchKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPath = z.ob export const zPatchKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdResponse = zKnowledgeFsSourceResponse -export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPath = z.object({ +export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewHeaders = z.object({ + 'Idempotency-Key': z.string(), +}) + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewPath = z.object({ control_space_id: z.string(), source_id: z.string(), }) /** - * KnowledgeFS source crawl + * KnowledgeFS source crawl preview accepted */ -export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlResponse = - zKnowledgeFsSourceCrawlResponse +export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdCrawlPreviewResponse = + zKnowledgeFsSourceWorkflowResponse export const zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdFilesPath = z.object({ control_space_id: z.string(), @@ -1931,6 +2520,46 @@ export const zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPagesQuery = export const zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdPagesResponse = zKnowledgeFsSourcePagesResponse +export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncHeaders = z.object({ + 'Idempotency-Key': z.string(), +}) + +export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPath = z.object({ + control_space_id: z.string(), + source_id: z.string(), +}) + +/** + * KnowledgeFS source sync accepted + */ +export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncResponse = + zKnowledgeFsSourceWorkflowResponse + +export const zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyPath = z.object({ + control_space_id: z.string(), + source_id: z.string(), +}) + +/** + * KnowledgeFS source sync policy + */ +export const zGetKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponse = + zKnowledgeFsSourceSyncPolicyResponse + +export const zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyBody = + zKnowledgeFsSourceSyncPolicyPayload + +export const zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyPath = z.object({ + control_space_id: z.string(), + source_id: z.string(), +}) + +/** + * KnowledgeFS source sync policy updated + */ +export const zPutKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdSyncPolicyResponse = + zKnowledgeFsSourceSyncPolicyResponse + export const zPostKnowledgeFsSpacesByControlSpaceIdSourcesBySourceIdTestPath = z.object({ control_space_id: z.string(), source_id: z.string(), @@ -2029,7 +2658,7 @@ export const zPostKnowledgeFsSpacesByControlSpaceIdUploadCapabilitiesResponse = export const zPostKnowledgeFsSpacesByControlSpaceIdUploadSessionsByUploadSessionIdSmallFileBody = z.object({ - file: z.custom(), + file: z.custom((value) => value instanceof Blob || value instanceof File), }) export const zPostKnowledgeFsSpacesByControlSpaceIdUploadSessionsByUploadSessionIdSmallFilePath = diff --git a/packages/contracts/generated/api/console/system-features/types.gen.ts b/packages/contracts/generated/api/console/system-features/types.gen.ts index 2f865c06736..51dace8e8bd 100644 --- a/packages/contracts/generated/api/console/system-features/types.gen.ts +++ b/packages/contracts/generated/api/console/system-features/types.gen.ts @@ -23,6 +23,7 @@ export type SystemFeatureModel = { is_allow_register: boolean is_email_setup: boolean knowledge_fs_enabled: boolean + knowledge_fs_upload_enabled: boolean license: LicenseStatusModel max_plugin_package_size: number plugin_installation_permission: PluginInstallationPermissionModel diff --git a/packages/contracts/generated/api/console/system-features/zod.gen.ts b/packages/contracts/generated/api/console/system-features/zod.gen.ts index 1ef07bee0bd..b8bcfbed3ce 100644 --- a/packages/contracts/generated/api/console/system-features/zod.gen.ts +++ b/packages/contracts/generated/api/console/system-features/zod.gen.ts @@ -135,6 +135,7 @@ export const zSystemFeatureModel = z.object({ is_allow_register: z.boolean().default(false), is_email_setup: z.boolean().default(false), knowledge_fs_enabled: z.boolean().default(false), + knowledge_fs_upload_enabled: z.boolean().default(false), license: zLicenseStatusModel.default({ status: 'none' }), max_plugin_package_size: z.int().default(15728640), plugin_installation_permission: zPluginInstallationPermissionModel.default({ diff --git a/packages/contracts/generated/api/web/types.gen.ts b/packages/contracts/generated/api/web/types.gen.ts index 3425dc64e5d..9dcbeaf9c18 100644 --- a/packages/contracts/generated/api/web/types.gen.ts +++ b/packages/contracts/generated/api/web/types.gen.ts @@ -520,6 +520,7 @@ export type SystemFeatureModel = { is_allow_register: boolean is_email_setup: boolean knowledge_fs_enabled: boolean + knowledge_fs_upload_enabled: boolean license: LicenseStatusModel max_plugin_package_size: number plugin_installation_permission: PluginInstallationPermissionModel diff --git a/packages/contracts/generated/api/web/zod.gen.ts b/packages/contracts/generated/api/web/zod.gen.ts index 515245a69e3..ab2c9e2429c 100644 --- a/packages/contracts/generated/api/web/zod.gen.ts +++ b/packages/contracts/generated/api/web/zod.gen.ts @@ -782,6 +782,7 @@ export const zSystemFeatureModel = z.object({ is_allow_register: z.boolean().default(false), is_email_setup: z.boolean().default(false), knowledge_fs_enabled: z.boolean().default(false), + knowledge_fs_upload_enabled: z.boolean().default(false), license: zLicenseStatusModel.default({ status: 'none' }), max_plugin_package_size: z.int().default(15728640), plugin_installation_permission: zPluginInstallationPermissionModel.default({ diff --git a/packages/contracts/generated/knowledge-fs/metadata.gen.ts b/packages/contracts/generated/knowledge-fs/metadata.gen.ts deleted file mode 100644 index 1b975bf9339..00000000000 --- a/packages/contracts/generated/knowledge-fs/metadata.gen.ts +++ /dev/null @@ -1,12 +0,0 @@ -// This file is auto-generated by scripts/generate-knowledge-fs-contract.mjs. -// Do not edit it manually. - -export const knowledgeFsSourceOpenapiSha256 = - 'f18910e9c45a64f0855e0643a7a626fb2889021b4f943458de86c6bd2469facb' -export const knowledgeFsConsoleDeclarationsSha256 = - '8bd1924747fdd0d478ca085817cbe000eb7e8630b2c6a03f4f13a6a0fac07946' -export const knowledgeFsGeneratedArtifactSha256 = { - 'orpc.gen.ts': 'e0d9954f817e97a4e95dd38c4522fb403c8659d741ce485fa671b1b4ce90a540', - 'types.gen.ts': 'a558ab80f32a8555bb5b44b7a596ef4a4a7a8cb7904390993aabcc587915f530', - 'zod.gen.ts': 'ca698a6fa64a0717e29da5d4678976b55355a2c0fe5ddfa7037325aa79ab4762', -} as const diff --git a/packages/contracts/generated/knowledge-fs/orpc.gen.ts b/packages/contracts/generated/knowledge-fs/orpc.gen.ts deleted file mode 100644 index 97816509fc2..00000000000 --- a/packages/contracts/generated/knowledge-fs/orpc.gen.ts +++ /dev/null @@ -1,1568 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { eventIterator, oc } from '@orpc/contract' -import * as z from 'zod' -import { - zCreateKnowledgeSpaceBody, - zCreateKnowledgeSpaceHeaders, - zCreateKnowledgeSpaceResponse, - zDeleteJobsByIdHeaders, - zDeleteJobsByIdPath, - zDeleteJobsByIdResponse, - zDeleteKnowledgeSpacesByIdBody, - zDeleteKnowledgeSpacesByIdDocumentsBulkBody, - zDeleteKnowledgeSpacesByIdDocumentsBulkHeaders, - zDeleteKnowledgeSpacesByIdDocumentsBulkPath, - zDeleteKnowledgeSpacesByIdDocumentsBulkResponse, - zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdBody, - zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdHeaders, - zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdPath, - zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdHeaders, - zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdPath, - zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponse, - zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdResponse, - zDeleteKnowledgeSpacesByIdHeaders, - zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdBody, - zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdHeaders, - zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdPath, - zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponse, - zDeleteKnowledgeSpacesByIdPath, - zDeleteKnowledgeSpacesByIdResponse, - zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdHeaders, - zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdPath, - zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdQuery, - zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponse, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdBody, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsHeaders, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsPath, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsQuery, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponse, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdHeaders, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdPath, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdQuery, - zDeleteKnowledgeSpacesByIdSourcesBySourceIdResponse, - zGetBulkJobsByIdHeaders, - zGetBulkJobsByIdPath, - zGetBulkJobsByIdResponse, - zGetDeletionJobsByJobIdHeaders, - zGetDeletionJobsByJobIdPath, - zGetDeletionJobsByJobIdResponse, - zGetJobsByIdHeaders, - zGetJobsByIdPath, - zGetJobsByIdResponse, - zGetKnowledgeSpacesByIdAccessPolicyHeaders, - zGetKnowledgeSpacesByIdAccessPolicyPath, - zGetKnowledgeSpacesByIdAccessPolicyResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlinePath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdPath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsPath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdPath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksPath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksQuery, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdPath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksPath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksQuery, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsPath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsQuery, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsResponse, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsHeaders, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsPath, - zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponse, - zGetKnowledgeSpacesByIdDocumentsHeaders, - zGetKnowledgeSpacesByIdDocumentsPath, - zGetKnowledgeSpacesByIdDocumentsQuery, - zGetKnowledgeSpacesByIdDocumentsResponse, - zGetKnowledgeSpacesByIdHeaders, - zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdHeaders, - zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdPath, - zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponse, - zGetKnowledgeSpacesByIdLogicalDocumentsHeaders, - zGetKnowledgeSpacesByIdLogicalDocumentsPath, - zGetKnowledgeSpacesByIdLogicalDocumentsQuery, - zGetKnowledgeSpacesByIdLogicalDocumentsResponse, - zGetKnowledgeSpacesByIdPath, - zGetKnowledgeSpacesByIdProcessingTasksHeaders, - zGetKnowledgeSpacesByIdProcessingTasksPath, - zGetKnowledgeSpacesByIdProcessingTasksQuery, - zGetKnowledgeSpacesByIdProcessingTasksResponse, - zGetKnowledgeSpacesByIdResponse, - zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdHeaders, - zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdPath, - zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponse, - zGetKnowledgeSpacesByIdSourceConnectionsHeaders, - zGetKnowledgeSpacesByIdSourceConnectionsPath, - zGetKnowledgeSpacesByIdSourceConnectionsQuery, - zGetKnowledgeSpacesByIdSourceConnectionsResponse, - zGetKnowledgeSpacesByIdSourcesBySourceIdFilesHeaders, - zGetKnowledgeSpacesByIdSourcesBySourceIdFilesPath, - zGetKnowledgeSpacesByIdSourcesBySourceIdFilesQuery, - zGetKnowledgeSpacesByIdSourcesBySourceIdFilesResponse, - zGetKnowledgeSpacesByIdSourcesBySourceIdHeaders, - zGetKnowledgeSpacesByIdSourcesBySourceIdPagesHeaders, - zGetKnowledgeSpacesByIdSourcesBySourceIdPagesPath, - zGetKnowledgeSpacesByIdSourcesBySourceIdPagesQuery, - zGetKnowledgeSpacesByIdSourcesBySourceIdPagesResponse, - zGetKnowledgeSpacesByIdSourcesBySourceIdPath, - zGetKnowledgeSpacesByIdSourcesBySourceIdResponse, - zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyHeaders, - zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyPath, - zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse, - zGetKnowledgeSpacesByIdSourcesHeaders, - zGetKnowledgeSpacesByIdSourcesPath, - zGetKnowledgeSpacesByIdSourcesQuery, - zGetKnowledgeSpacesByIdSourcesResponse, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsHeaders, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsPath, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsQuery, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsResponse, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdHeaders, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesHeaders, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesPath, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesQuery, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponse, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPath, - zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdResponse, - zGetKnowledgeSpacesByIdSourceWorkflowsHeaders, - zGetKnowledgeSpacesByIdSourceWorkflowsPath, - zGetKnowledgeSpacesByIdSourceWorkflowsQuery, - zGetKnowledgeSpacesByIdSourceWorkflowsResponse, - zGetKnowledgeSpacesByIdStatsHeaders, - zGetKnowledgeSpacesByIdStatsPath, - zGetKnowledgeSpacesByIdStatsQuery, - zGetKnowledgeSpacesByIdStatsResponse, - zGetSourceProvidersHeaders, - zGetSourceProvidersResponse, - zListKnowledgeSpacesHeaders, - zListKnowledgeSpacesQuery, - zListKnowledgeSpacesResponse, - zPatchKnowledgeSpacesByIdAccessPolicyBody, - zPatchKnowledgeSpacesByIdAccessPolicyHeaders, - zPatchKnowledgeSpacesByIdAccessPolicyPath, - zPatchKnowledgeSpacesByIdAccessPolicyResponse, - zPatchKnowledgeSpacesByIdBody, - zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataBody, - zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataHeaders, - zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataPath, - zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataResponse, - zPatchKnowledgeSpacesByIdHeaders, - zPatchKnowledgeSpacesByIdPath, - zPatchKnowledgeSpacesByIdResponse, - zPatchKnowledgeSpacesByIdSourcesBySourceIdBody, - zPatchKnowledgeSpacesByIdSourcesBySourceIdHeaders, - zPatchKnowledgeSpacesByIdSourcesBySourceIdPath, - zPatchKnowledgeSpacesByIdSourcesBySourceIdResponse, - zPostDeletionJobsByJobIdRetryHeaders, - zPostDeletionJobsByJobIdRetryPath, - zPostDeletionJobsByJobIdRetryResponse, - zPostJobsByIdRetryHeaders, - zPostJobsByIdRetryPath, - zPostJobsByIdRetryResponse, - zPostKnowledgeSpacesByIdDocumentsBody, - zPostKnowledgeSpacesByIdDocumentsBulkBody, - zPostKnowledgeSpacesByIdDocumentsBulkHeaders, - zPostKnowledgeSpacesByIdDocumentsBulkPath, - zPostKnowledgeSpacesByIdDocumentsBulkReindexBody, - zPostKnowledgeSpacesByIdDocumentsBulkReindexHeaders, - zPostKnowledgeSpacesByIdDocumentsBulkReindexPath, - zPostKnowledgeSpacesByIdDocumentsBulkReindexResponse, - zPostKnowledgeSpacesByIdDocumentsBulkResponse, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryHeaders, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryPath, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryResponse, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateBody, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateHeaders, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStatePath, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateResponse, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackBody, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackHeaders, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackPath, - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackResponse, - zPostKnowledgeSpacesByIdDocumentsHeaders, - zPostKnowledgeSpacesByIdDocumentsPath, - zPostKnowledgeSpacesByIdDocumentsResponse, - zPostKnowledgeSpacesByIdSourceConnectionsBody, - zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshBody, - zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshHeaders, - zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshPath, - zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshResponse, - zPostKnowledgeSpacesByIdSourceConnectionsHeaders, - zPostKnowledgeSpacesByIdSourceConnectionsOauthBody, - zPostKnowledgeSpacesByIdSourceConnectionsOauthHeaders, - zPostKnowledgeSpacesByIdSourceConnectionsOauthPath, - zPostKnowledgeSpacesByIdSourceConnectionsOauthResponse, - zPostKnowledgeSpacesByIdSourceConnectionsPath, - zPostKnowledgeSpacesByIdSourceConnectionsResponse, - zPostKnowledgeSpacesByIdSourcesBody, - zPostKnowledgeSpacesByIdSourcesBulkBody, - zPostKnowledgeSpacesByIdSourcesBulkHeaders, - zPostKnowledgeSpacesByIdSourcesBulkPath, - zPostKnowledgeSpacesByIdSourcesBulkResponse, - zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlHeaders, - zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPath, - zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewHeaders, - zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewPath, - zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewResponse, - zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlResponse, - zPostKnowledgeSpacesByIdSourcesBySourceIdImportBody, - zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesBody, - zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesHeaders, - zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesPath, - zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesResponse, - zPostKnowledgeSpacesByIdSourcesBySourceIdImportHeaders, - zPostKnowledgeSpacesByIdSourcesBySourceIdImportPath, - zPostKnowledgeSpacesByIdSourcesBySourceIdImportResponse, - zPostKnowledgeSpacesByIdSourcesBySourceIdSyncHeaders, - zPostKnowledgeSpacesByIdSourcesBySourceIdSyncPath, - zPostKnowledgeSpacesByIdSourcesBySourceIdSyncResponse, - zPostKnowledgeSpacesByIdSourcesBySourceIdTestHeaders, - zPostKnowledgeSpacesByIdSourcesBySourceIdTestPath, - zPostKnowledgeSpacesByIdSourcesBySourceIdTestResponse, - zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsBody, - zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsHeaders, - zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsPath, - zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsResponse, - zPostKnowledgeSpacesByIdSourcesHeaders, - zPostKnowledgeSpacesByIdSourcesPath, - zPostKnowledgeSpacesByIdSourcesResponse, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelBody, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelHeaders, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelPath, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelResponse, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryHeaders, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryPath, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryResponse, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionBody, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionHeaders, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionPath, - zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionResponse, - zPostSourceOauthCallbackBody, - zPostSourceOauthCallbackHeaders, - zPostSourceOauthCallbackResponse, - zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsBody, - zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsHeaders, - zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsPath, - zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponse, - zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsBody, - zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsHeaders, - zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsPath, - zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponse, - zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyBody, - zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyHeaders, - zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyPath, - zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse, -} from './zod.gen' - -export const listKnowledgeSpaces = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'listKnowledgeSpaces', - path: '/knowledge-fs/knowledge-spaces', - tags: ['Knowledge Spaces'], - }) - .input( - z.object({ - headers: zListKnowledgeSpacesHeaders.optional(), - query: zListKnowledgeSpacesQuery.optional(), - }), - ) - .output(zListKnowledgeSpacesResponse) - -export const createKnowledgeSpace = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'createKnowledgeSpace', - path: '/knowledge-fs/knowledge-spaces', - successStatus: 201, - tags: ['Knowledge Spaces'], - }) - .input( - z.object({ body: zCreateKnowledgeSpaceBody, headers: zCreateKnowledgeSpaceHeaders.optional() }), - ) - .output(zCreateKnowledgeSpaceResponse) - -export const deleteKnowledgeSpacesById = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteKnowledgeSpacesById', - path: '/knowledge-fs/knowledge-spaces/{id}', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zDeleteKnowledgeSpacesByIdBody, - headers: zDeleteKnowledgeSpacesByIdHeaders, - params: zDeleteKnowledgeSpacesByIdPath, - }), - ) - .output(zDeleteKnowledgeSpacesByIdResponse) - -export const getKnowledgeSpacesById = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesById', - path: '/knowledge-fs/knowledge-spaces/{id}', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdHeaders.optional(), - params: zGetKnowledgeSpacesByIdPath, - }), - ) - .output(zGetKnowledgeSpacesByIdResponse) - -export const patchKnowledgeSpacesById = oc - .route({ - inputStructure: 'detailed', - method: 'PATCH', - operationId: 'patchKnowledgeSpacesById', - path: '/knowledge-fs/knowledge-spaces/{id}', - tags: ['default'], - }) - .input( - z.object({ - body: zPatchKnowledgeSpacesByIdBody, - headers: zPatchKnowledgeSpacesByIdHeaders.optional(), - params: zPatchKnowledgeSpacesByIdPath, - }), - ) - .output(zPatchKnowledgeSpacesByIdResponse) - -export const getKnowledgeSpacesByIdStats = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdStats', - path: '/knowledge-fs/knowledge-spaces/{id}/stats', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdStatsHeaders.optional(), - params: zGetKnowledgeSpacesByIdStatsPath, - query: zGetKnowledgeSpacesByIdStatsQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdStatsResponse) - -export const getKnowledgeSpacesByIdAccessPolicy = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdAccessPolicy', - path: '/knowledge-fs/knowledge-spaces/{id}/access-policy', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdAccessPolicyHeaders.optional(), - params: zGetKnowledgeSpacesByIdAccessPolicyPath, - }), - ) - .output(zGetKnowledgeSpacesByIdAccessPolicyResponse) - -export const patchKnowledgeSpacesByIdAccessPolicy = oc - .route({ - inputStructure: 'detailed', - method: 'PATCH', - operationId: 'patchKnowledgeSpacesByIdAccessPolicy', - path: '/knowledge-fs/knowledge-spaces/{id}/access-policy', - tags: ['default'], - }) - .input( - z.object({ - body: zPatchKnowledgeSpacesByIdAccessPolicyBody, - headers: zPatchKnowledgeSpacesByIdAccessPolicyHeaders.optional(), - params: zPatchKnowledgeSpacesByIdAccessPolicyPath, - }), - ) - .output(zPatchKnowledgeSpacesByIdAccessPolicyResponse) - -export const getSourceProviders = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getSourceProviders', - path: '/knowledge-fs/source-providers', - tags: ['default'], - }) - .input(z.object({ headers: zGetSourceProvidersHeaders.optional() })) - .output(zGetSourceProvidersResponse) - -export const getKnowledgeSpacesByIdSourceConnections = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourceConnections', - path: '/knowledge-fs/knowledge-spaces/{id}/source-connections', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourceConnectionsHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourceConnectionsPath, - query: zGetKnowledgeSpacesByIdSourceConnectionsQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdSourceConnectionsResponse) - -export const postKnowledgeSpacesByIdSourceConnections = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourceConnections', - path: '/knowledge-fs/knowledge-spaces/{id}/source-connections', - successStatus: 201, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourceConnectionsBody, - headers: zPostKnowledgeSpacesByIdSourceConnectionsHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourceConnectionsPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourceConnectionsResponse) - -export const postKnowledgeSpacesByIdSourceConnectionsOauth = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourceConnectionsOauth', - path: '/knowledge-fs/knowledge-spaces/{id}/source-connections/oauth', - successStatus: 201, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourceConnectionsOauthBody, - headers: zPostKnowledgeSpacesByIdSourceConnectionsOauthHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourceConnectionsOauthPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourceConnectionsOauthResponse) - -export const postSourceOauthCallback = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postSourceOauthCallback', - path: '/knowledge-fs/source-oauth/callback', - tags: ['default'], - }) - .input( - z.object({ - body: zPostSourceOauthCallbackBody, - headers: zPostSourceOauthCallbackHeaders.optional(), - }), - ) - .output(zPostSourceOauthCallbackResponse) - -export const deleteKnowledgeSpacesByIdSourceConnectionsByConnectionId = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteKnowledgeSpacesByIdSourceConnectionsByConnectionId', - path: '/knowledge-fs/knowledge-spaces/{id}/source-connections/{connectionId}', - tags: ['default'], - }) - .input( - z.object({ - headers: zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdHeaders.optional(), - params: zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdPath, - query: zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdQuery, - }), - ) - .output(zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponse) - -export const getKnowledgeSpacesByIdSourceConnectionsByConnectionId = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourceConnectionsByConnectionId', - path: '/knowledge-fs/knowledge-spaces/{id}/source-connections/{connectionId}', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdPath, - }), - ) - .output(zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponse) - -export const postKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefresh = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefresh', - path: '/knowledge-fs/knowledge-spaces/{id}/source-connections/{connectionId}/refresh', - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshBody, - headers: zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshResponse) - -export const getKnowledgeSpacesByIdSources = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSources', - path: '/knowledge-fs/knowledge-spaces/{id}/sources', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourcesHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourcesPath, - query: zGetKnowledgeSpacesByIdSourcesQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdSourcesResponse) - -export const postKnowledgeSpacesByIdSources = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSources', - path: '/knowledge-fs/knowledge-spaces/{id}/sources', - successStatus: 201, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourcesBody, - headers: zPostKnowledgeSpacesByIdSourcesHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourcesPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesResponse) - -export const deleteKnowledgeSpacesByIdSourcesBySourceId = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteKnowledgeSpacesByIdSourcesBySourceId', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zDeleteKnowledgeSpacesByIdSourcesBySourceIdBody, - headers: zDeleteKnowledgeSpacesByIdSourcesBySourceIdHeaders, - params: zDeleteKnowledgeSpacesByIdSourcesBySourceIdPath, - query: zDeleteKnowledgeSpacesByIdSourcesBySourceIdQuery.optional(), - }), - ) - .output(zDeleteKnowledgeSpacesByIdSourcesBySourceIdResponse) - -export const getKnowledgeSpacesByIdSourcesBySourceId = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourcesBySourceId', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourcesBySourceIdHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourcesBySourceIdPath, - }), - ) - .output(zGetKnowledgeSpacesByIdSourcesBySourceIdResponse) - -export const patchKnowledgeSpacesByIdSourcesBySourceId = oc - .route({ - inputStructure: 'detailed', - method: 'PATCH', - operationId: 'patchKnowledgeSpacesByIdSourcesBySourceId', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}', - tags: ['default'], - }) - .input( - z.object({ - body: zPatchKnowledgeSpacesByIdSourcesBySourceIdBody, - headers: zPatchKnowledgeSpacesByIdSourcesBySourceIdHeaders.optional(), - params: zPatchKnowledgeSpacesByIdSourcesBySourceIdPath, - }), - ) - .output(zPatchKnowledgeSpacesByIdSourcesBySourceIdResponse) - -export const deleteKnowledgeSpacesByIdSourcesBySourceIdCredentials = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteKnowledgeSpacesByIdSourcesBySourceIdCredentials', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/credentials', - tags: ['default'], - }) - .input( - z.object({ - headers: zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsHeaders.optional(), - params: zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsPath, - query: zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsQuery, - }), - ) - .output(zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponse) - -export const putKnowledgeSpacesByIdSourcesBySourceIdCredentials = oc - .route({ - inputStructure: 'detailed', - method: 'PUT', - operationId: 'putKnowledgeSpacesByIdSourcesBySourceIdCredentials', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/credentials', - tags: ['default'], - }) - .input( - z.object({ - body: zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsBody, - headers: zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsHeaders.optional(), - params: zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsPath, - }), - ) - .output(zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponse) - -export const postKnowledgeSpacesByIdSourcesBySourceIdSync = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourcesBySourceIdSync', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/sync', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - headers: zPostKnowledgeSpacesByIdSourcesBySourceIdSyncHeaders, - params: zPostKnowledgeSpacesByIdSourcesBySourceIdSyncPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesBySourceIdSyncResponse) - -export const postKnowledgeSpacesByIdSourcesBySourceIdCrawlPreview = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourcesBySourceIdCrawlPreview', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/crawl-preview', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - headers: zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewHeaders, - params: zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewResponse) - -export const postKnowledgeSpacesByIdSourcesBySourceIdWorkflowImports = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourcesBySourceIdWorkflowImports', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/workflow-imports', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsBody, - headers: zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsHeaders, - params: zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsResponse) - -export const getKnowledgeSpacesByIdSourcesBySourceIdPages = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourcesBySourceIdPages', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/pages', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourcesBySourceIdPagesHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourcesBySourceIdPagesPath, - query: zGetKnowledgeSpacesByIdSourcesBySourceIdPagesQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdSourcesBySourceIdPagesResponse) - -export const getKnowledgeSpacesByIdSourcesBySourceIdFiles = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourcesBySourceIdFiles', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/files', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourcesBySourceIdFilesHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourcesBySourceIdFilesPath, - query: zGetKnowledgeSpacesByIdSourcesBySourceIdFilesQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdSourcesBySourceIdFilesResponse) - -export const postKnowledgeSpacesByIdSourcesBySourceIdCrawl = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourcesBySourceIdCrawl', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/crawl', - tags: ['default'], - }) - .input( - z.object({ - headers: zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlResponse) - -export const postKnowledgeSpacesByIdSourcesBySourceIdImport = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourcesBySourceIdImport', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/import', - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourcesBySourceIdImportBody, - headers: zPostKnowledgeSpacesByIdSourcesBySourceIdImportHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourcesBySourceIdImportPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesBySourceIdImportResponse) - -export const postKnowledgeSpacesByIdSourcesBySourceIdTest = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourcesBySourceIdTest', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/test', - tags: ['default'], - }) - .input( - z.object({ - headers: zPostKnowledgeSpacesByIdSourcesBySourceIdTestHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourcesBySourceIdTestPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesBySourceIdTestResponse) - -export const postKnowledgeSpacesByIdSourcesBySourceIdImportFiles = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourcesBySourceIdImportFiles', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/import-files', - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesBody, - headers: zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesResponse) - -export const postKnowledgeSpacesByIdSourcesBulk = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourcesBulk', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/bulk', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourcesBulkBody, - headers: zPostKnowledgeSpacesByIdSourcesBulkHeaders, - params: zPostKnowledgeSpacesByIdSourcesBulkPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourcesBulkResponse) - -export const getKnowledgeSpacesByIdSourceWorkflows = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourceWorkflows', - path: '/knowledge-fs/knowledge-spaces/{id}/source-workflows', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourceWorkflowsHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourceWorkflowsPath, - query: zGetKnowledgeSpacesByIdSourceWorkflowsQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdSourceWorkflowsResponse) - -export const getKnowledgeSpacesByIdSourceWorkflowsByRunId = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourceWorkflowsByRunId', - path: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPath, - }), - ) - .output(zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdResponse) - -export const getKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItems = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItems', - path: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/bulk-items', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsPath, - query: zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsResponse) - -export const getKnowledgeSpacesByIdSourceWorkflowsByRunIdPages = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourceWorkflowsByRunIdPages', - path: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/pages', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesPath, - query: zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponse) - -export const postKnowledgeSpacesByIdSourceWorkflowsByRunIdCancel = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourceWorkflowsByRunIdCancel', - path: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/cancel', - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelBody, - headers: zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelResponse) - -export const postKnowledgeSpacesByIdSourceWorkflowsByRunIdRetry = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourceWorkflowsByRunIdRetry', - path: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/retry', - tags: ['default'], - }) - .input( - z.object({ - headers: zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryHeaders.optional(), - params: zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryResponse) - -export const postKnowledgeSpacesByIdSourceWorkflowsByRunIdSelection = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdSourceWorkflowsByRunIdSelection', - path: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/selection', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionBody, - headers: zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionHeaders, - params: zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionPath, - }), - ) - .output(zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionResponse) - -export const getKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/sync-policy', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyHeaders.optional(), - params: zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyPath, - }), - ) - .output(zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse) - -export const putKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy = oc - .route({ - inputStructure: 'detailed', - method: 'PUT', - operationId: 'putKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy', - path: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/sync-policy', - tags: ['default'], - }) - .input( - z.object({ - body: zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyBody, - headers: zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyHeaders.optional(), - params: zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyPath, - }), - ) - .output(zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse) - -export const getKnowledgeSpacesByIdDocuments = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocuments', - path: '/knowledge-fs/knowledge-spaces/{id}/documents', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdDocumentsHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsPath, - query: zGetKnowledgeSpacesByIdDocumentsQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsResponse) - -export const postKnowledgeSpacesByIdDocuments = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdDocuments', - path: '/knowledge-fs/knowledge-spaces/{id}/documents', - successStatus: 201, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdDocumentsBody, - headers: zPostKnowledgeSpacesByIdDocumentsHeaders.optional(), - params: zPostKnowledgeSpacesByIdDocumentsPath, - }), - ) - .output(zPostKnowledgeSpacesByIdDocumentsResponse) - -export const deleteKnowledgeSpacesByIdDocumentsBulk = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteKnowledgeSpacesByIdDocumentsBulk', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/bulk', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zDeleteKnowledgeSpacesByIdDocumentsBulkBody, - headers: zDeleteKnowledgeSpacesByIdDocumentsBulkHeaders, - params: zDeleteKnowledgeSpacesByIdDocumentsBulkPath, - }), - ) - .output(zDeleteKnowledgeSpacesByIdDocumentsBulkResponse) - -export const postKnowledgeSpacesByIdDocumentsBulk = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdDocumentsBulk', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/bulk', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdDocumentsBulkBody, - headers: zPostKnowledgeSpacesByIdDocumentsBulkHeaders.optional(), - params: zPostKnowledgeSpacesByIdDocumentsBulkPath, - }), - ) - .output(zPostKnowledgeSpacesByIdDocumentsBulkResponse) - -export const postKnowledgeSpacesByIdDocumentsBulkReindex = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdDocumentsBulkReindex', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/bulk/reindex', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdDocumentsBulkReindexBody, - headers: zPostKnowledgeSpacesByIdDocumentsBulkReindexHeaders.optional(), - params: zPostKnowledgeSpacesByIdDocumentsBulkReindexPath, - }), - ) - .output(zPostKnowledgeSpacesByIdDocumentsBulkReindexResponse) - -export const deleteKnowledgeSpacesByIdDocumentsByDocumentId = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteKnowledgeSpacesByIdDocumentsByDocumentId', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdBody, - headers: zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdHeaders, - params: zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdPath, - }), - ) - .output(zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentId = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentId', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdDocumentsByDocumentIdHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdPath, - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsByDocumentIdResponse) - -export const getKnowledgeSpacesByIdLogicalDocuments = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdLogicalDocuments', - path: '/knowledge-fs/knowledge-spaces/{id}/logical-documents', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdLogicalDocumentsHeaders.optional(), - params: zGetKnowledgeSpacesByIdLogicalDocumentsPath, - query: zGetKnowledgeSpacesByIdLogicalDocumentsQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdLogicalDocumentsResponse) - -export const deleteKnowledgeSpacesByIdLogicalDocumentsByDocumentId = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteKnowledgeSpacesByIdLogicalDocumentsByDocumentId', - path: '/knowledge-fs/knowledge-spaces/{id}/logical-documents/{documentId}', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdBody, - headers: zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdHeaders, - params: zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdPath, - }), - ) - .output(zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponse) - -export const getKnowledgeSpacesByIdLogicalDocumentsByDocumentId = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdLogicalDocumentsByDocumentId', - path: '/knowledge-fs/knowledge-spaces/{id}/logical-documents/{documentId}', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdHeaders.optional(), - params: zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdPath, - }), - ) - .output(zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentIdOutline = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentIdOutline', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/outline', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlinePath, - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentIdRevisions = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentIdRevisions', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsPath, - query: zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsResponse) - -export const postKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollback = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollback', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions/{revision}/rollback', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackBody, - headers: - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackHeaders.optional(), - params: zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackPath, - }), - ) - .output(zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackResponse) - -export const patchKnowledgeSpacesByIdDocumentsByDocumentIdMetadata = oc - .route({ - inputStructure: 'detailed', - method: 'PATCH', - operationId: 'patchKnowledgeSpacesByIdDocumentsByDocumentIdMetadata', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/metadata', - tags: ['default'], - }) - .input( - z.object({ - body: zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataBody, - headers: zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataHeaders.optional(), - params: zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataPath, - }), - ) - .output(zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunks = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunks', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions/{revision}/chunks', - tags: ['default'], - }) - .input( - z.object({ - headers: - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksPath, - query: zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkId = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkId', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions/{revision}/chunks/{chunkId}', - tags: ['default'], - }) - .input( - z.object({ - headers: - zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdPath, - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdResponse) - -export const postKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdState = - oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: - 'postKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdState', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions/{revision}/chunks/{chunkId}/state', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateBody, - headers: - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateHeaders.optional(), - params: - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStatePath, - }), - ) - .output( - zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateResponse, - ) - -export const getKnowledgeSpacesByIdProcessingTasks = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdProcessingTasks', - path: '/knowledge-fs/knowledge-spaces/{id}/processing-tasks', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdProcessingTasksHeaders.optional(), - params: zGetKnowledgeSpacesByIdProcessingTasksPath, - query: zGetKnowledgeSpacesByIdProcessingTasksQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdProcessingTasksResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasks = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasks', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksPath, - query: zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksQuery.optional(), - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksResponse) - -export const deleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks/{taskId}', - tags: ['default'], - }) - .input( - z.object({ - headers: - zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdHeaders.optional(), - params: zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdPath, - }), - ) - .output(zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks/{taskId}', - tags: ['default'], - }) - .input( - z.object({ - headers: - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdPath, - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEvents = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEvents', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks/{taskId}/events', - tags: ['default'], - }) - .input( - z.object({ - headers: - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsPath, - }), - ) - .output( - eventIterator( - zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsResponse, - ), - ) - -export const postKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetry = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetry', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks/{taskId}/retry', - tags: ['default'], - }) - .input( - z.object({ - headers: - zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryHeaders.optional(), - params: zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryPath, - }), - ) - .output(zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryResponse) - -export const getKnowledgeSpacesByIdDocumentsByDocumentIdSettings = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getKnowledgeSpacesByIdDocumentsByDocumentIdSettings', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/settings', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsHeaders.optional(), - params: zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsPath, - }), - ) - .output(zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponse) - -export const putKnowledgeSpacesByIdDocumentsByDocumentIdSettings = oc - .route({ - inputStructure: 'detailed', - method: 'PUT', - operationId: 'putKnowledgeSpacesByIdDocumentsByDocumentIdSettings', - path: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/settings', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - body: zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsBody, - headers: zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsHeaders.optional(), - params: zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsPath, - }), - ) - .output(zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponse) - -export const deleteJobsById = oc - .route({ - inputStructure: 'detailed', - method: 'DELETE', - operationId: 'deleteJobsById', - path: '/knowledge-fs/jobs/{id}', - tags: ['default'], - }) - .input(z.object({ headers: zDeleteJobsByIdHeaders.optional(), params: zDeleteJobsByIdPath })) - .output(zDeleteJobsByIdResponse) - -export const getJobsById = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getJobsById', - path: '/knowledge-fs/jobs/{id}', - tags: ['default'], - }) - .input(z.object({ headers: zGetJobsByIdHeaders.optional(), params: zGetJobsByIdPath })) - .output(zGetJobsByIdResponse) - -export const postJobsByIdRetry = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postJobsByIdRetry', - path: '/knowledge-fs/jobs/{id}/retry', - tags: ['default'], - }) - .input( - z.object({ headers: zPostJobsByIdRetryHeaders.optional(), params: zPostJobsByIdRetryPath }), - ) - .output(zPostJobsByIdRetryResponse) - -export const getDeletionJobsByJobId = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getDeletionJobsByJobId', - path: '/knowledge-fs/deletion-jobs/{jobId}', - tags: ['default'], - }) - .input( - z.object({ - headers: zGetDeletionJobsByJobIdHeaders.optional(), - params: zGetDeletionJobsByJobIdPath, - }), - ) - .output(zGetDeletionJobsByJobIdResponse) - -export const postDeletionJobsByJobIdRetry = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postDeletionJobsByJobIdRetry', - path: '/knowledge-fs/deletion-jobs/{jobId}/retry', - successStatus: 202, - tags: ['default'], - }) - .input( - z.object({ - headers: zPostDeletionJobsByJobIdRetryHeaders, - params: zPostDeletionJobsByJobIdRetryPath, - }), - ) - .output(zPostDeletionJobsByJobIdRetryResponse) - -export const getBulkJobsById = oc - .route({ - inputStructure: 'detailed', - method: 'GET', - operationId: 'getBulkJobsById', - path: '/knowledge-fs/bulk-jobs/{id}', - tags: ['default'], - }) - .input(z.object({ headers: zGetBulkJobsByIdHeaders.optional(), params: zGetBulkJobsByIdPath })) - .output(zGetBulkJobsByIdResponse) - -export const contract = { - listKnowledgeSpaces, - createKnowledgeSpace, - deleteKnowledgeSpacesById, - getKnowledgeSpacesById, - patchKnowledgeSpacesById, - getKnowledgeSpacesByIdStats, - getKnowledgeSpacesByIdAccessPolicy, - patchKnowledgeSpacesByIdAccessPolicy, - getSourceProviders, - getKnowledgeSpacesByIdSourceConnections, - postKnowledgeSpacesByIdSourceConnections, - postKnowledgeSpacesByIdSourceConnectionsOauth, - postSourceOauthCallback, - deleteKnowledgeSpacesByIdSourceConnectionsByConnectionId, - getKnowledgeSpacesByIdSourceConnectionsByConnectionId, - postKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefresh, - getKnowledgeSpacesByIdSources, - postKnowledgeSpacesByIdSources, - deleteKnowledgeSpacesByIdSourcesBySourceId, - getKnowledgeSpacesByIdSourcesBySourceId, - patchKnowledgeSpacesByIdSourcesBySourceId, - deleteKnowledgeSpacesByIdSourcesBySourceIdCredentials, - putKnowledgeSpacesByIdSourcesBySourceIdCredentials, - postKnowledgeSpacesByIdSourcesBySourceIdSync, - postKnowledgeSpacesByIdSourcesBySourceIdCrawlPreview, - postKnowledgeSpacesByIdSourcesBySourceIdWorkflowImports, - getKnowledgeSpacesByIdSourcesBySourceIdPages, - getKnowledgeSpacesByIdSourcesBySourceIdFiles, - postKnowledgeSpacesByIdSourcesBySourceIdCrawl, - postKnowledgeSpacesByIdSourcesBySourceIdImport, - postKnowledgeSpacesByIdSourcesBySourceIdTest, - postKnowledgeSpacesByIdSourcesBySourceIdImportFiles, - postKnowledgeSpacesByIdSourcesBulk, - getKnowledgeSpacesByIdSourceWorkflows, - getKnowledgeSpacesByIdSourceWorkflowsByRunId, - getKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItems, - getKnowledgeSpacesByIdSourceWorkflowsByRunIdPages, - postKnowledgeSpacesByIdSourceWorkflowsByRunIdCancel, - postKnowledgeSpacesByIdSourceWorkflowsByRunIdRetry, - postKnowledgeSpacesByIdSourceWorkflowsByRunIdSelection, - getKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy, - putKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy, - getKnowledgeSpacesByIdDocuments, - postKnowledgeSpacesByIdDocuments, - deleteKnowledgeSpacesByIdDocumentsBulk, - postKnowledgeSpacesByIdDocumentsBulk, - postKnowledgeSpacesByIdDocumentsBulkReindex, - deleteKnowledgeSpacesByIdDocumentsByDocumentId, - getKnowledgeSpacesByIdDocumentsByDocumentId, - getKnowledgeSpacesByIdLogicalDocuments, - deleteKnowledgeSpacesByIdLogicalDocumentsByDocumentId, - getKnowledgeSpacesByIdLogicalDocumentsByDocumentId, - getKnowledgeSpacesByIdDocumentsByDocumentIdOutline, - getKnowledgeSpacesByIdDocumentsByDocumentIdRevisions, - postKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollback, - patchKnowledgeSpacesByIdDocumentsByDocumentIdMetadata, - getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunks, - getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkId, - postKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdState, - getKnowledgeSpacesByIdProcessingTasks, - getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasks, - deleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId, - getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId, - getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEvents, - postKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetry, - getKnowledgeSpacesByIdDocumentsByDocumentIdSettings, - putKnowledgeSpacesByIdDocumentsByDocumentIdSettings, - deleteJobsById, - getJobsById, - postJobsByIdRetry, - getDeletionJobsByJobId, - postDeletionJobsByJobIdRetry, - getBulkJobsById, -} diff --git a/packages/contracts/generated/knowledge-fs/types.gen.ts b/packages/contracts/generated/knowledge-fs/types.gen.ts deleted file mode 100644 index dc7be0b1fa7..00000000000 --- a/packages/contracts/generated/knowledge-fs/types.gen.ts +++ /dev/null @@ -1,3302 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type ClientOptions = { - baseUrl: `${string}://${string}` | (string & {}) -} - -export type KnowledgeSpaceCreationResponse = { - createdAt: string - description?: string - iconRef?: string - id: string - name: string - revision: number - slug: string - tenantId: string - updatedAt: string - configurationStatus: 'pending-validation' | 'ready' | 'setup-required' | 'validation-failed' -} - -export type ErrorResponse = { - code?: string - error: string -} - -export type CreateKnowledgeSpace = { - description?: string - embeddingProfile?: { - model: string - pluginId: string - provider: string - } - iconRef?: string - idempotencyKey?: string - name: string - retrievalProfile?: { - defaultMode: 'fast' | 'research' | 'deep' - reasoningModel: { - model: string - pluginId: string - provider: string - } - rerank: { - enabled: boolean - model?: { - model: string - pluginId: string - provider: string - } - } - scoreThreshold: { - enabled: boolean - stage: 'mode-final' | 'rerank' - value?: number - } - topK: number - } - slug?: string -} - -export type KnowledgeSpace = { - createdAt: string - description?: string - iconRef?: string - id: string - name: string - revision: number - slug: string - tenantId: string - updatedAt: string -} - -export type KnowledgeSpaceList = { - items: Array - nextCursor?: string -} - -export type KnowledgeSpaceStats = { - cache: { - available: boolean - entries: number - totalBytes: number - } - commits: { - failedRetryable: number - failedTerminal: number - sampled: number - truncated: boolean - } - generatedAt: string - knowledgeSpaceId: string - metrics: { - available: boolean - reason?: string - } - projections: { - denseVector: { - building: number - failed: number - ready: number - stale: number - total: number - } - fts: { - building: number - failed: number - ready: number - stale: number - total: number - } - graph: { - building: number - failed: number - ready: number - stale: number - total: number - } - metadata: { - building: number - failed: number - ready: number - stale: number - total: number - } - projectionVersion: number - } - runtime: { - activeLeaseSampleCount: number - activeSessionSampleCount: number - truncated: boolean - } - storage: { - documentCount: number - rawDocumentBytes: number - } - tenantId: string - window: { - end: string - minutes: number - start: string - } -} - -export type DurableDeletionJob = { - checkpoint: - | 'requested' - | 'quiescing' - | 'deleting_objects' - | 'deleting_derived_data' - | 'deleting_primary_data' - | 'completed' - completedAt?: string - createdAt: string - error?: { - code: string - message: string - retryable: boolean - } - id: string - knowledgeSpaceId: string - mode?: 'cascade' | 'keep' - progress?: { - completedItems: number - currentItemKind?: string - totalItems?: number - } - retryAt?: string - runState: - | 'dispatch_pending' - | 'queued' - | 'running' - | 'retry_wait' - | 'completed' - | 'failed' - | 'canceled' - targetId: string - targetType: 'knowledge_space' | 'source' | 'document' | 'logical_document' - updatedAt: string -} - -export type DurableDeletionAccepted = { - job: DurableDeletionJob - statusUrl: string -} - -export type DurableBulkDeletionAccepted = { - items: Array<{ - documentId: string - job: DurableDeletionJob - statusUrl: string - }> - total: number -} - -export type DocumentAsset = { - createdAt: string - filename: string - id: string - knowledgeSpaceId: string - metadata?: { - [key: string]: unknown - } - mimeType: string - objectKey: string - parserStatus: 'pending' | 'parsed' | 'failed' - sha256: string - sizeBytes: number - sourceId?: string - updatedAt?: string - version: number -} - -export type DocumentAssetList = { - items: Array - nextCursor?: string -} - -export type DocumentOutlineNode = { - childNodeIds?: Array - children?: Array<{ - [key: string]: unknown - }> - endOffset?: number - endPage?: number - id: string - level: number - metadata: { - [key: string]: unknown - } - sectionPath?: Array - sourceElementIds?: Array - sourceNodeIds?: Array - startOffset?: number - startPage?: number - summary?: string - title: string - titleLocation?: { - [key: string]: unknown - } - tocSource: string -} - -export type DocumentOutline = { - artifactHash: string - createdAt: string - documentAssetId: string - id: string - knowledgeSpaceId: string - metadata: { - [key: string]: unknown - } - nodes: Array - outlineVersion: string - parseArtifactId: string - updatedAt?: string - version: number -} - -export type LogicalDocumentRevision = { - activatedAt?: string - contentHash: string - createdAt: string - documentAssetId: string - documentAssetVersion: number - documentId: string - knowledgeSpaceId: string - mimeType: string - revision: number - sizeBytes: number - state: 'candidate' | 'active' | 'superseded' | 'failed' -} | null - -export type LogicalDocument = { - active: LogicalDocumentRevision - activeRevision?: number - createdAt: string - id: string - knowledgeSpaceId: string - providerItemId?: string - rowVersion: number - sourceId?: string - status: 'pending' | 'ready' | 'failed' | 'deleting' - title: string - updatedAt: string - userMetadata: { - [key: string]: unknown - } -} - -export type LogicalDocumentList = { - items: Array - nextCursor?: string -} - -export type DocumentRevisionList = { - items: Array< - LogicalDocumentRevision & { - [key: string]: unknown - } - > - nextCursor?: string -} - -export type DocumentProcessingTask = { - completedAt?: string - createdAt: string - documentId: string - documentRevision: number - errorCode?: string - errorMessage?: string - id: string - knowledgeSpaceId: string - progressPercent: number - retryAt?: string - stage: - | 'queued' - | 'parsed' - | 'outline_built' - | 'nodes_generated' - | 'projection_built' - | 'smoke_eval_passed' - | 'published' - state: - | 'dispatch_pending' - | 'queued' - | 'running' - | 'retry_wait' - | 'succeeded' - | 'failed' - | 'canceled' - | 'superseded' - updatedAt: string -} - -export type DocumentRevisionChunk = { - createdAt: string - documentId: string - documentRevision: number - enabled: boolean - id: string - knowledgeSpaceId: string - ordinal: number - parentChunkId?: string - text: string - tokenCount: number - userMetadata: { - [key: string]: unknown - } -} - -export type DocumentChunkList = { - items: Array - nextCursor?: string -} - -export type DocumentChunkStateChangeAccepted = { - candidateFingerprint?: string - candidatePublicationId?: string - chunkId: string - compilationAttemptId: string - createdAt: string - documentId: string - documentRevision: number - enabled: boolean - id: string - knowledgeSpaceId: string - state: 'candidate' - statusUrl: string -} - -export type DocumentProcessingTaskList = { - items: Array - nextCursor?: string -} - -export type DocumentProcessingTaskEvent = - | { - data: { - progressPercent: number - stage: - | 'queued' - | 'parsed' - | 'outline_built' - | 'nodes_generated' - | 'projection_built' - | 'smoke_eval_passed' - | 'published' - state: - | 'dispatch_pending' - | 'queued' - | 'running' - | 'retry_wait' - | 'succeeded' - | 'failed' - | 'canceled' - | 'superseded' - updatedAt: string - } - event: 'progress' - } - | { - data: { - errorCode?: string - state: 'succeeded' | 'failed' | 'canceled' | 'superseded' - } - event: 'terminal' - } - -export type DocumentSettingsHead = { - activeRevision: number - profile: { - activatedAt?: string - createdAt: string - revision: number - settings: { - chunkOverlap: number - chunkSize: number - enableGraph: boolean - enablePageIndex: boolean - language?: string - } - state: 'active' - } - rowVersion: number - updatedAt: string -} - -export type DocumentReindexAccepted = { - attemptId: string - compilationAttemptId: string - settingsRevision: number - state: 'running' - statusUrl: string -} - -export type DocumentCompilationJob = { - baseHeadRevision?: number - candidateFingerprint?: string - candidatePublicationId?: string - completedAt?: number - createdAt: number - documentAssetId: string - error?: string - executionAttempts?: number - id: string - knowledgeSpaceId: string - leaseExpiresAt?: number - maxExecutionAttempts?: number - publicationGenerationId?: string - queueJobId?: string - retryAt?: number - runState?: - | 'dispatch_pending' - | 'queued' - | 'running' - | 'retry_wait' - | 'succeeded' - | 'failed' - | 'canceled' - | 'superseded' - stage: - | 'queued' - | 'parsed' - | 'outline_built' - | 'nodes_generated' - | 'projection_built' - | 'smoke_eval_passed' - | 'published' - | 'failed' - | 'canceled' - tenantId: string - updatedAt: number - version: number -} - -export type BulkOperationProgress = { - completedItems: number - createdAt: string - failedItemIds: Array - failedItems: number - id: string - knowledgeSpaceId: string - status: 'running' | 'completed' | 'failed' - totalItems: number - type: 'document_upload' | 'document_delete' | 'document_reindex' - updatedAt: string -} - -export type BulkDocumentReindexResult = { - bulkJobId: string - items: Array< - | { - asset: DocumentAsset - compilationJob: { - id: string - stage: 'queued' - } - status: 'queued' - statusUrl: string - } - | { - documentId: string - status: 'not_found' - } - > - total: number -} - -export type DocumentUploadAccepted = { - asset: DocumentAsset - assetStatusUrl?: string - compilationJob: { - id: string - stage: 'queued' - } - logicalDocument: { - id: string - revision: number - } - logicalDocumentId: string - documentRevision: number - statusUrl: string - status?: 'accepted' -} - -export type BulkDocumentUploadAccepted = { - accepted: number - bulkJobId: string - excluded: number - items: Array< - | DocumentUploadAccepted - | { - filename: string - index: number - mimeType: string - reason: - | 'batch_byte_limit_exceeded' - | 'document_not_found' - | 'file_count_limit_exceeded' - | 'file_too_large' - | 'invalid_file' - | 'invalid_target' - | 'processing_failed' - | 'quota_exceeded' - | 'revision_conflict' - | 'unsupported_mime_type' - sizeBytes: number - status: 'excluded' - } - > - total: number -} - -export type SourceWorkflowRun = { - canceledAt?: string - checkpoint: string - completedAt?: string - createdAt: string - cursor?: string - executionAttempts: number - id: string - knowledgeSpaceId: string - kind: string - lastErrorCode?: string - maxExecutionAttempts: number - progressCompleted: number - progressFailed: number - progressSkipped: number - progressTotal?: number - sourceId?: string - state: string - updatedAt: string -} - -export type Source = { - connectionId?: string - createdAt: string - id: string - knowledgeSpaceId: string - metadata: { - [key: string]: unknown - } - name: string - permissionScope?: Array - status: 'active' | 'syncing' | 'error' | 'disabled' - type: 'upload' | 'object-storage' | 'connector' | 'web' - updatedAt: string - uri: string - version?: number - credentialConfigured?: boolean -} - -export type WebsiteCrawlResult = { - completed?: number - failed?: number - imported?: number - pages: Array<{ - content: string - description?: string - sourceUrl: string - title?: string - }> - replaced?: number - skipped?: number - status?: string - total?: number -} - -export type OnlineDocumentPages = { - nextCursor?: string - workspaces: Array<{ - pages: Array<{ - lastEditedTime?: string - pageId: string - pageName: string - parentId?: string - type: string - }> - total?: number - workspaceId?: string - workspaceName?: string - }> -} - -export type SourceImportResult = { - documents: Array<{ - documentAssetId: string - filename: string - }> - failed: Array<{ - code: string - error: string - filename: string - }> - skipped: Array -} - -export type SourceCredentialTest = { - code?: string - error?: string - valid: boolean -} - -export type OnlineDriveFiles = { - buckets: Array<{ - bucket?: string - continuationToken?: string - files: Array<{ - id: string - name: string - size?: number - type: string - }> - isTruncated?: boolean - }> -} - -export type ConsoleProxyError = { - code: string - message: string - status: number -} - -export type ListKnowledgeSpacesData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path?: never - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces' -} - -export type ListKnowledgeSpacesErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 502: ConsoleProxyError -} - -export type ListKnowledgeSpacesError = ListKnowledgeSpacesErrors[keyof ListKnowledgeSpacesErrors] - -export type ListKnowledgeSpacesResponses = { - 200: KnowledgeSpaceList -} - -export type ListKnowledgeSpacesResponse = - ListKnowledgeSpacesResponses[keyof ListKnowledgeSpacesResponses] - -export type CreateKnowledgeSpaceData = { - body: CreateKnowledgeSpace - headers?: { - 'x-trace-id'?: string - } - path?: never - query?: never - url: '/knowledge-fs/knowledge-spaces' -} - -export type CreateKnowledgeSpaceErrors = { - 400: - | { - code: 'RETRIEVAL_PROFILE_SCORE_THRESHOLD_REQUIRES_RERANK' - error: 'Fast/Deep mode-final score threshold requires the knowledge-space reranker to be enabled' - mode: 'fast' | 'research' | 'deep' - } - | ErrorResponse - 403: ConsoleProxyError - 409: ErrorResponse - 422: ErrorResponse - 429: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type CreateKnowledgeSpaceError = CreateKnowledgeSpaceErrors[keyof CreateKnowledgeSpaceErrors] - -export type CreateKnowledgeSpaceResponses = { - 201: KnowledgeSpaceCreationResponse -} - -export type CreateKnowledgeSpaceResponse = - CreateKnowledgeSpaceResponses[keyof CreateKnowledgeSpaceResponses] - -export type DeleteKnowledgeSpacesByIdData = { - body: { - challenge: string - expectedRevision: number - } - headers: { - 'idempotency-key': string - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}' -} - -export type DeleteKnowledgeSpacesByIdErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type DeleteKnowledgeSpacesByIdError = - DeleteKnowledgeSpacesByIdErrors[keyof DeleteKnowledgeSpacesByIdErrors] - -export type DeleteKnowledgeSpacesByIdResponses = { - 202: DurableDeletionAccepted -} - -export type DeleteKnowledgeSpacesByIdResponse = - DeleteKnowledgeSpacesByIdResponses[keyof DeleteKnowledgeSpacesByIdResponses] - -export type GetKnowledgeSpacesByIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}' -} - -export type GetKnowledgeSpacesByIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdError = - GetKnowledgeSpacesByIdErrors[keyof GetKnowledgeSpacesByIdErrors] - -export type GetKnowledgeSpacesByIdResponses = { - 200: KnowledgeSpace -} - -export type GetKnowledgeSpacesByIdResponse = - GetKnowledgeSpacesByIdResponses[keyof GetKnowledgeSpacesByIdResponses] - -export type PatchKnowledgeSpacesByIdData = { - body: { - description?: string - expectedRevision: number - iconRef?: string | null - name?: string - slug?: string - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}' -} - -export type PatchKnowledgeSpacesByIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PatchKnowledgeSpacesByIdError = - PatchKnowledgeSpacesByIdErrors[keyof PatchKnowledgeSpacesByIdErrors] - -export type PatchKnowledgeSpacesByIdResponses = { - 200: KnowledgeSpace -} - -export type PatchKnowledgeSpacesByIdResponse = - PatchKnowledgeSpacesByIdResponses[keyof PatchKnowledgeSpacesByIdResponses] - -export type GetKnowledgeSpacesByIdStatsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: { - windowMinutes?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/stats' -} - -export type GetKnowledgeSpacesByIdStatsErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdStatsError = - GetKnowledgeSpacesByIdStatsErrors[keyof GetKnowledgeSpacesByIdStatsErrors] - -export type GetKnowledgeSpacesByIdStatsResponses = { - 200: KnowledgeSpaceStats -} - -export type GetKnowledgeSpacesByIdStatsResponse = - GetKnowledgeSpacesByIdStatsResponses[keyof GetKnowledgeSpacesByIdStatsResponses] - -export type GetKnowledgeSpacesByIdAccessPolicyData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/access-policy' -} - -export type GetKnowledgeSpacesByIdAccessPolicyErrors = { - 400: ErrorResponse & { - [key: string]: unknown - } - 403: ConsoleProxyError - 404: ErrorResponse & { - [key: string]: unknown - } - 409: ErrorResponse & { - [key: string]: unknown - } - 429: ErrorResponse & { - [key: string]: unknown - } - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdAccessPolicyError = - GetKnowledgeSpacesByIdAccessPolicyErrors[keyof GetKnowledgeSpacesByIdAccessPolicyErrors] - -export type GetKnowledgeSpacesByIdAccessPolicyResponses = { - 200: { - id: string - ownerSubjectId: string - partialMemberSubjectIds: Array - revision: number - visibility: 'only_me' | 'all_members' | 'partial_members' - } -} - -export type GetKnowledgeSpacesByIdAccessPolicyResponse = - GetKnowledgeSpacesByIdAccessPolicyResponses[keyof GetKnowledgeSpacesByIdAccessPolicyResponses] - -export type PatchKnowledgeSpacesByIdAccessPolicyData = { - body: { - expectedRevision: number - partialMemberSubjectIds?: Array - visibility: 'only_me' | 'all_members' | 'partial_members' - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/access-policy' -} - -export type PatchKnowledgeSpacesByIdAccessPolicyErrors = { - 400: ErrorResponse & { - [key: string]: unknown - } - 403: ConsoleProxyError - 404: ErrorResponse & { - [key: string]: unknown - } - 409: ErrorResponse & { - [key: string]: unknown - } - 429: ErrorResponse & { - [key: string]: unknown - } - 502: ConsoleProxyError -} - -export type PatchKnowledgeSpacesByIdAccessPolicyError = - PatchKnowledgeSpacesByIdAccessPolicyErrors[keyof PatchKnowledgeSpacesByIdAccessPolicyErrors] - -export type PatchKnowledgeSpacesByIdAccessPolicyResponses = { - 200: { - id: string - ownerSubjectId: string - partialMemberSubjectIds: Array - revision: number - visibility: 'only_me' | 'all_members' | 'partial_members' - } -} - -export type PatchKnowledgeSpacesByIdAccessPolicyResponse = - PatchKnowledgeSpacesByIdAccessPolicyResponses[keyof PatchKnowledgeSpacesByIdAccessPolicyResponses] - -export type GetSourceProvidersData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path?: never - query?: never - url: '/knowledge-fs/source-providers' -} - -export type GetSourceProvidersErrors = { - 403: ConsoleProxyError - 502: ConsoleProxyError -} - -export type GetSourceProvidersError = GetSourceProvidersErrors[keyof GetSourceProvidersErrors] - -export type GetSourceProvidersResponses = { - 200: { - items: Array<{ - authKinds: Array<'api-key' | 'endpoint' | 'oauth2'> - available: boolean - capabilities: Array<'website-crawl' | 'online-document' | 'online-drive'> - configuration: Array<{ - description?: string - format?: 'password' | 'uri' - name: string - required: boolean - secret: boolean - type: 'boolean' | 'integer' | 'string' - }> - displayName: string - id: string - unavailableReason?: string - }> - } -} - -export type GetSourceProvidersResponse = - GetSourceProvidersResponses[keyof GetSourceProvidersResponses] - -export type GetKnowledgeSpacesByIdSourceConnectionsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/source-connections' -} - -export type GetKnowledgeSpacesByIdSourceConnectionsErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourceConnectionsError = - GetKnowledgeSpacesByIdSourceConnectionsErrors[keyof GetKnowledgeSpacesByIdSourceConnectionsErrors] - -export type GetKnowledgeSpacesByIdSourceConnectionsResponses = { - 200: { - items: Array<{ - authKind: 'api-key' | 'endpoint' | 'oauth2' - configuration: { - [key: string]: boolean | number | string - } - createdAt: string - errorCode?: string - expiresAt?: string - id: string - knowledgeSpaceId: string - name: string - providerId: string - scopes: Array - status: 'provisioning' | 'active' | 'expired' | 'error' | 'revoked' - updatedAt: string - version: number - }> - nextCursor?: string - } -} - -export type GetKnowledgeSpacesByIdSourceConnectionsResponse = - GetKnowledgeSpacesByIdSourceConnectionsResponses[keyof GetKnowledgeSpacesByIdSourceConnectionsResponses] - -export type PostKnowledgeSpacesByIdSourceConnectionsData = { - body: { - authKind: 'api-key' | 'endpoint' - configuration?: { - [key: string]: boolean | number | string - } - credentials: { - [key: string]: unknown - } - name: string - providerId: string - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/source-connections' -} - -export type PostKnowledgeSpacesByIdSourceConnectionsErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ErrorResponse | ConsoleProxyError - 503: ErrorResponse -} - -export type PostKnowledgeSpacesByIdSourceConnectionsError = - PostKnowledgeSpacesByIdSourceConnectionsErrors[keyof PostKnowledgeSpacesByIdSourceConnectionsErrors] - -export type PostKnowledgeSpacesByIdSourceConnectionsResponses = { - 201: { - authKind: 'api-key' | 'endpoint' | 'oauth2' - configuration: { - [key: string]: boolean | number | string - } - createdAt: string - errorCode?: string - expiresAt?: string - id: string - knowledgeSpaceId: string - name: string - providerId: string - scopes: Array - status: 'provisioning' | 'active' | 'expired' | 'error' | 'revoked' - updatedAt: string - version: number - } -} - -export type PostKnowledgeSpacesByIdSourceConnectionsResponse = - PostKnowledgeSpacesByIdSourceConnectionsResponses[keyof PostKnowledgeSpacesByIdSourceConnectionsResponses] - -export type PostKnowledgeSpacesByIdSourceConnectionsOauthData = { - body: { - configuration?: { - [key: string]: boolean | number | string - } - name: string - providerId: string - redirectUri: string - scopes?: Array - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/source-connections/oauth' -} - -export type PostKnowledgeSpacesByIdSourceConnectionsOauthErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 502: ErrorResponse | ConsoleProxyError - 503: ErrorResponse -} - -export type PostKnowledgeSpacesByIdSourceConnectionsOauthError = - PostKnowledgeSpacesByIdSourceConnectionsOauthErrors[keyof PostKnowledgeSpacesByIdSourceConnectionsOauthErrors] - -export type PostKnowledgeSpacesByIdSourceConnectionsOauthResponses = { - 201: { - authorizationUrl: string - connection: { - authKind: 'api-key' | 'endpoint' | 'oauth2' - configuration: { - [key: string]: boolean | number | string - } - createdAt: string - errorCode?: string - expiresAt?: string - id: string - knowledgeSpaceId: string - name: string - providerId: string - scopes: Array - status: 'provisioning' | 'active' | 'expired' | 'error' | 'revoked' - updatedAt: string - version: number - } - } -} - -export type PostKnowledgeSpacesByIdSourceConnectionsOauthResponse = - PostKnowledgeSpacesByIdSourceConnectionsOauthResponses[keyof PostKnowledgeSpacesByIdSourceConnectionsOauthResponses] - -export type PostSourceOauthCallbackData = { - body: { - code: string - state: string - } - headers?: { - 'x-trace-id'?: string - } - path?: never - query?: never - url: '/knowledge-fs/source-oauth/callback' -} - -export type PostSourceOauthCallbackErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 409: ErrorResponse - 502: ErrorResponse | ConsoleProxyError - 503: ErrorResponse -} - -export type PostSourceOauthCallbackError = - PostSourceOauthCallbackErrors[keyof PostSourceOauthCallbackErrors] - -export type PostSourceOauthCallbackResponses = { - 200: { - authKind: 'api-key' | 'endpoint' | 'oauth2' - configuration: { - [key: string]: boolean | number | string - } - createdAt: string - errorCode?: string - expiresAt?: string - id: string - knowledgeSpaceId: string - name: string - providerId: string - scopes: Array - status: 'provisioning' | 'active' | 'expired' | 'error' | 'revoked' - updatedAt: string - version: number - } -} - -export type PostSourceOauthCallbackResponse = - PostSourceOauthCallbackResponses[keyof PostSourceOauthCallbackResponses] - -export type DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - connectionId: string - } - query: { - expectedVersion: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/source-connections/{connectionId}' -} - -export type DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdError = - DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdErrors[keyof DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdErrors] - -export type DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponses = { - 200: { - authKind: 'api-key' | 'endpoint' | 'oauth2' - configuration: { - [key: string]: boolean | number | string - } - createdAt: string - errorCode?: string - expiresAt?: string - id: string - knowledgeSpaceId: string - name: string - providerId: string - scopes: Array - status: 'provisioning' | 'active' | 'expired' | 'error' | 'revoked' - updatedAt: string - version: number - } -} - -export type DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponse = - DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponses[keyof DeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponses] - -export type GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - connectionId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/source-connections/{connectionId}' -} - -export type GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdError = - GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdErrors[keyof GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdErrors] - -export type GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponses = { - 200: { - authKind: 'api-key' | 'endpoint' | 'oauth2' - configuration: { - [key: string]: boolean | number | string - } - createdAt: string - errorCode?: string - expiresAt?: string - id: string - knowledgeSpaceId: string - name: string - providerId: string - scopes: Array - status: 'provisioning' | 'active' | 'expired' | 'error' | 'revoked' - updatedAt: string - version: number - } -} - -export type GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponse = - GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponses[keyof GetKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponses] - -export type PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshData = { - body: { - expectedVersion: number - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - connectionId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/source-connections/{connectionId}/refresh' -} - -export type PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ErrorResponse | ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshError = - PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshErrors[keyof PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshErrors] - -export type PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshResponses = { - 200: { - authKind: 'api-key' | 'endpoint' | 'oauth2' - configuration: { - [key: string]: boolean | number | string - } - createdAt: string - errorCode?: string - expiresAt?: string - id: string - knowledgeSpaceId: string - name: string - providerId: string - scopes: Array - status: 'provisioning' | 'active' | 'expired' | 'error' | 'revoked' - updatedAt: string - version: number - } -} - -export type PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshResponse = - PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshResponses[keyof PostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshResponses] - -export type GetKnowledgeSpacesByIdSourcesData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/sources' -} - -export type GetKnowledgeSpacesByIdSourcesErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError - 503: { - code: 'CANDIDATE_VISIBILITY_SCAN_BUDGET_EXCEEDED' - error: 'Candidate visibility scan budget exceeded' - } -} - -export type GetKnowledgeSpacesByIdSourcesError = - GetKnowledgeSpacesByIdSourcesErrors[keyof GetKnowledgeSpacesByIdSourcesErrors] - -export type GetKnowledgeSpacesByIdSourcesResponses = { - 200: { - items: Array - nextCursor?: string - } -} - -export type GetKnowledgeSpacesByIdSourcesResponse = - GetKnowledgeSpacesByIdSourcesResponses[keyof GetKnowledgeSpacesByIdSourcesResponses] - -export type PostKnowledgeSpacesByIdSourcesData = { - body: { - connectionId?: string - credentials?: { - [key: string]: unknown - } - metadata?: { - [key: string]: unknown - } - name: string - permissionScope?: Array - status?: 'active' | 'syncing' | 'error' | 'disabled' - type: 'upload' | 'object-storage' | 'connector' | 'web' - uri: string - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources' -} - -export type PostKnowledgeSpacesByIdSourcesErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 429: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PostKnowledgeSpacesByIdSourcesError = - PostKnowledgeSpacesByIdSourcesErrors[keyof PostKnowledgeSpacesByIdSourcesErrors] - -export type PostKnowledgeSpacesByIdSourcesResponses = { - 201: Source -} - -export type PostKnowledgeSpacesByIdSourcesResponse = - PostKnowledgeSpacesByIdSourcesResponses[keyof PostKnowledgeSpacesByIdSourcesResponses] - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdData = { - body: { - expectedRevision: number - } - headers: { - 'idempotency-key': string - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: { - documents?: 'cascade' | 'keep' - } - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}' -} - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdError = - DeleteKnowledgeSpacesByIdSourcesBySourceIdErrors[keyof DeleteKnowledgeSpacesByIdSourcesBySourceIdErrors] - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdResponses = { - 202: DurableDeletionAccepted -} - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdResponse = - DeleteKnowledgeSpacesByIdSourcesBySourceIdResponses[keyof DeleteKnowledgeSpacesByIdSourcesBySourceIdResponses] - -export type GetKnowledgeSpacesByIdSourcesBySourceIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}' -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdError = - GetKnowledgeSpacesByIdSourcesBySourceIdErrors[keyof GetKnowledgeSpacesByIdSourcesBySourceIdErrors] - -export type GetKnowledgeSpacesByIdSourcesBySourceIdResponses = { - 200: Source -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdResponse = - GetKnowledgeSpacesByIdSourcesBySourceIdResponses[keyof GetKnowledgeSpacesByIdSourcesBySourceIdResponses] - -export type PatchKnowledgeSpacesByIdSourcesBySourceIdData = { - body: { - expectedVersion?: number - metadata?: { - [key: string]: unknown - } - name?: string - status?: 'active' | 'syncing' | 'error' | 'disabled' - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}' -} - -export type PatchKnowledgeSpacesByIdSourcesBySourceIdErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PatchKnowledgeSpacesByIdSourcesBySourceIdError = - PatchKnowledgeSpacesByIdSourcesBySourceIdErrors[keyof PatchKnowledgeSpacesByIdSourcesBySourceIdErrors] - -export type PatchKnowledgeSpacesByIdSourcesBySourceIdResponses = { - 200: Source -} - -export type PatchKnowledgeSpacesByIdSourcesBySourceIdResponse = - PatchKnowledgeSpacesByIdSourcesBySourceIdResponses[keyof PatchKnowledgeSpacesByIdSourcesBySourceIdResponses] - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query: { - expectedVersion: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/credentials' -} - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsError = - DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsErrors[keyof DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsErrors] - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponses = { - 200: Source -} - -export type DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponse = - DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponses[keyof DeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponses] - -export type PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsData = { - body: { - credentials: { - [key: string]: unknown - } - expectedVersion: number - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/credentials' -} - -export type PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsError = - PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsErrors[keyof PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsErrors] - -export type PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponses = { - 200: Source -} - -export type PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponse = - PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponses[keyof PutKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponses] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdSyncData = { - body?: never - headers: { - 'Idempotency-Key': string - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/sync' -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdSyncErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdSyncError = - PostKnowledgeSpacesByIdSourcesBySourceIdSyncErrors[keyof PostKnowledgeSpacesByIdSourcesBySourceIdSyncErrors] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdSyncResponses = { - 202: SourceWorkflowRun -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdSyncResponse = - PostKnowledgeSpacesByIdSourcesBySourceIdSyncResponses[keyof PostKnowledgeSpacesByIdSourcesBySourceIdSyncResponses] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewData = { - body?: never - headers: { - 'Idempotency-Key': string - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/crawl-preview' -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewError = - PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewErrors[keyof PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewErrors] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewResponses = { - 202: SourceWorkflowRun -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewResponse = - PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewResponses[keyof PostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewResponses] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsData = { - body: - | { - items: Array<{ - etag?: string - lastEditedTime?: string - name?: string - pageId: string - providerItemId: string - type: string - workspaceId: string - }> - kind: 'online-document-import' - } - | { - items: Array<{ - bucket?: string - etag?: string - id: string - mimeType?: string - name: string - providerItemId: string - }> - kind: 'online-drive-import' - } - headers: { - 'Idempotency-Key': string - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/workflow-imports' -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsError = - PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsErrors[keyof PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsErrors] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsResponses = { - 202: SourceWorkflowRun -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsResponse = - PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsResponses[keyof PostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsResponses] - -export type GetKnowledgeSpacesByIdSourcesBySourceIdPagesData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/pages' -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdPagesErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 501: ErrorResponse - 502: ErrorResponse | ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdPagesError = - GetKnowledgeSpacesByIdSourcesBySourceIdPagesErrors[keyof GetKnowledgeSpacesByIdSourcesBySourceIdPagesErrors] - -export type GetKnowledgeSpacesByIdSourcesBySourceIdPagesResponses = { - 200: OnlineDocumentPages -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdPagesResponse = - GetKnowledgeSpacesByIdSourcesBySourceIdPagesResponses[keyof GetKnowledgeSpacesByIdSourcesBySourceIdPagesResponses] - -export type GetKnowledgeSpacesByIdSourcesBySourceIdFilesData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: { - bucket?: string - continuationToken?: string - maxKeys?: number - prefix?: string - } - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/files' -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdFilesErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 501: ErrorResponse - 502: ErrorResponse | ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdFilesError = - GetKnowledgeSpacesByIdSourcesBySourceIdFilesErrors[keyof GetKnowledgeSpacesByIdSourcesBySourceIdFilesErrors] - -export type GetKnowledgeSpacesByIdSourcesBySourceIdFilesResponses = { - 200: OnlineDriveFiles -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdFilesResponse = - GetKnowledgeSpacesByIdSourcesBySourceIdFilesResponses[keyof GetKnowledgeSpacesByIdSourcesBySourceIdFilesResponses] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/crawl' -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 501: ErrorResponse - 502: ErrorResponse | ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlError = - PostKnowledgeSpacesByIdSourcesBySourceIdCrawlErrors[keyof PostKnowledgeSpacesByIdSourcesBySourceIdCrawlErrors] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlResponses = { - 200: WebsiteCrawlResult -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdCrawlResponse = - PostKnowledgeSpacesByIdSourcesBySourceIdCrawlResponses[keyof PostKnowledgeSpacesByIdSourcesBySourceIdCrawlResponses] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportData = { - body: { - pages: Array<{ - lastEditedTime?: string - name?: string - pageId: string - type: string - workspaceId: string - }> - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/import' -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 501: ErrorResponse - 502: ErrorResponse | ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportError = - PostKnowledgeSpacesByIdSourcesBySourceIdImportErrors[keyof PostKnowledgeSpacesByIdSourcesBySourceIdImportErrors] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportResponses = { - 200: SourceImportResult -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportResponse = - PostKnowledgeSpacesByIdSourcesBySourceIdImportResponses[keyof PostKnowledgeSpacesByIdSourcesBySourceIdImportResponses] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdTestData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/test' -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdTestErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 501: ErrorResponse - 502: ErrorResponse | ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdTestError = - PostKnowledgeSpacesByIdSourcesBySourceIdTestErrors[keyof PostKnowledgeSpacesByIdSourcesBySourceIdTestErrors] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdTestResponses = { - 200: SourceCredentialTest -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdTestResponse = - PostKnowledgeSpacesByIdSourcesBySourceIdTestResponses[keyof PostKnowledgeSpacesByIdSourcesBySourceIdTestResponses] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesData = { - body: { - files: Array<{ - bucket?: string - id: string - mimeType?: string - name: string - }> - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/import-files' -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 501: ErrorResponse - 502: ErrorResponse | ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesError = - PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesErrors[keyof PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesErrors] - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesResponses = { - 200: SourceImportResult -} - -export type PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesResponse = - PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesResponses[keyof PostKnowledgeSpacesByIdSourcesBySourceIdImportFilesResponses] - -export type PostKnowledgeSpacesByIdSourcesBulkData = { - body: { - action: 'sync' | 'disable' | 'remove' - sourceIds: Array - } - headers: { - 'Idempotency-Key': string - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/bulk' -} - -export type PostKnowledgeSpacesByIdSourcesBulkErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourcesBulkError = - PostKnowledgeSpacesByIdSourcesBulkErrors[keyof PostKnowledgeSpacesByIdSourcesBulkErrors] - -export type PostKnowledgeSpacesByIdSourcesBulkResponses = { - 202: SourceWorkflowRun -} - -export type PostKnowledgeSpacesByIdSourcesBulkResponse = - PostKnowledgeSpacesByIdSourcesBulkResponses[keyof PostKnowledgeSpacesByIdSourcesBulkResponses] - -export type GetKnowledgeSpacesByIdSourceWorkflowsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: { - cursor?: string - limit?: number - sourceId?: string - } - url: '/knowledge-fs/knowledge-spaces/{id}/source-workflows' -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsError = - GetKnowledgeSpacesByIdSourceWorkflowsErrors[keyof GetKnowledgeSpacesByIdSourceWorkflowsErrors] - -export type GetKnowledgeSpacesByIdSourceWorkflowsResponses = { - 200: { - items: Array - nextCursor?: string - } -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsResponse = - GetKnowledgeSpacesByIdSourceWorkflowsResponses[keyof GetKnowledgeSpacesByIdSourceWorkflowsResponses] - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - runId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}' -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdError = - GetKnowledgeSpacesByIdSourceWorkflowsByRunIdErrors[keyof GetKnowledgeSpacesByIdSourceWorkflowsByRunIdErrors] - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdResponses = { - 200: SourceWorkflowRun -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdResponse = - GetKnowledgeSpacesByIdSourceWorkflowsByRunIdResponses[keyof GetKnowledgeSpacesByIdSourceWorkflowsByRunIdResponses] - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - runId: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/bulk-items' -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsError = - GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsErrors[keyof GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsErrors] - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsResponses = { - 200: { - items: Array<{ - action: 'sync' | 'disable' | 'remove' - errorCode?: string - id: string - reason?: string - sourceId: string - status: 'eligible' | 'running' | 'skipped' | 'failed' | 'completed' - updatedAt: string - }> - nextCursor?: string - } -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsResponse = - GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsResponses[keyof GetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsResponses] - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - runId: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/pages' -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesError = - GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesErrors[keyof GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesErrors] - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponses = { - 200: { - items: Array<{ - description?: string - etag?: string - pageId: string - sourceUrl: string - title?: string - }> - nextCursor?: string - } -} - -export type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponse = - GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponses[keyof GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponses] - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelData = { - body: { - reason?: string - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - runId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/cancel' -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelError = - PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelErrors[keyof PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelErrors] - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelResponses = { - 200: SourceWorkflowRun -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelResponse = - PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelResponses[keyof PostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelResponses] - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - runId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/retry' -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryError = - PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryErrors[keyof PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryErrors] - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryResponses = { - 200: SourceWorkflowRun -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryResponse = - PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryResponses[keyof PostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryResponses] - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionData = { - body: { - pageIds: Array - } - headers: { - 'Idempotency-Key': string - 'x-trace-id'?: string - } - path: { - id: string - runId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/source-workflows/{runId}/selection' -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionError = - PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionErrors[keyof PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionErrors] - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionResponses = { - 202: SourceWorkflowRun -} - -export type PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionResponse = - PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionResponses[keyof PostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionResponses] - -export type GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/sync-policy' -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyError = - GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyErrors[keyof GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyErrors] - -export type GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponses = { - 200: { - createdAt: string - customIntervalSeconds?: number - enabled: boolean - expectedSourceVersion: number - id: string - knowledgeSpaceId: string - mode: 'provider' | 'manual' | 'interval' | 'custom' - nextRunAt?: string - revision: number - sourceId: string - updatedAt: string - } -} - -export type GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse = - GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponses[keyof GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponses] - -export type PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyData = { - body: { - customIntervalSeconds?: number - enabled: boolean - expectedRevision: number - expectedSourceVersion: number - mode: 'provider' | 'manual' | 'interval' | 'custom' - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - sourceId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/sources/{sourceId}/sync-policy' -} - -export type PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyError = - PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyErrors[keyof PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyErrors] - -export type PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponses = { - 200: { - createdAt: string - customIntervalSeconds?: number - enabled: boolean - expectedSourceVersion: number - id: string - knowledgeSpaceId: string - mode: 'provider' | 'manual' | 'interval' | 'custom' - nextRunAt?: string - revision: number - sourceId: string - updatedAt: string - } -} - -export type PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse = - PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponses[keyof PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponses] - -export type GetKnowledgeSpacesByIdDocumentsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/documents' -} - -export type GetKnowledgeSpacesByIdDocumentsErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError - 503: { - code: 'CANDIDATE_VISIBILITY_SCAN_BUDGET_EXCEEDED' - error: 'Candidate visibility scan budget exceeded' - } -} - -export type GetKnowledgeSpacesByIdDocumentsError = - GetKnowledgeSpacesByIdDocumentsErrors[keyof GetKnowledgeSpacesByIdDocumentsErrors] - -export type GetKnowledgeSpacesByIdDocumentsResponses = { - 200: DocumentAssetList -} - -export type GetKnowledgeSpacesByIdDocumentsResponse = - GetKnowledgeSpacesByIdDocumentsResponses[keyof GetKnowledgeSpacesByIdDocumentsResponses] - -export type PostKnowledgeSpacesByIdDocumentsData = { - body: { - documentId?: string - expectedActiveRevision?: number | 'null' - expectedDocumentRowVersion?: number | null - file: Blob | File - sourceId?: string - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents' -} - -export type PostKnowledgeSpacesByIdDocumentsErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 413: ErrorResponse - 429: ErrorResponse - 500: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PostKnowledgeSpacesByIdDocumentsError = - PostKnowledgeSpacesByIdDocumentsErrors[keyof PostKnowledgeSpacesByIdDocumentsErrors] - -export type PostKnowledgeSpacesByIdDocumentsResponses = { - 201: DocumentAsset - 202: DocumentUploadAccepted -} - -export type PostKnowledgeSpacesByIdDocumentsResponse = - PostKnowledgeSpacesByIdDocumentsResponses[keyof PostKnowledgeSpacesByIdDocumentsResponses] - -export type DeleteKnowledgeSpacesByIdDocumentsBulkData = { - body: { - documents: Array<{ - documentId: string - expectedRevision: number - }> - } - headers: { - 'idempotency-key': string - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/bulk' -} - -export type DeleteKnowledgeSpacesByIdDocumentsBulkErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type DeleteKnowledgeSpacesByIdDocumentsBulkError = - DeleteKnowledgeSpacesByIdDocumentsBulkErrors[keyof DeleteKnowledgeSpacesByIdDocumentsBulkErrors] - -export type DeleteKnowledgeSpacesByIdDocumentsBulkResponses = { - 202: DurableBulkDeletionAccepted -} - -export type DeleteKnowledgeSpacesByIdDocumentsBulkResponse = - DeleteKnowledgeSpacesByIdDocumentsBulkResponses[keyof DeleteKnowledgeSpacesByIdDocumentsBulkResponses] - -export type PostKnowledgeSpacesByIdDocumentsBulkData = { - body: { - files: Array - targets?: string - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/bulk' -} - -export type PostKnowledgeSpacesByIdDocumentsBulkErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 413: ErrorResponse - 429: ErrorResponse - 500: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PostKnowledgeSpacesByIdDocumentsBulkError = - PostKnowledgeSpacesByIdDocumentsBulkErrors[keyof PostKnowledgeSpacesByIdDocumentsBulkErrors] - -export type PostKnowledgeSpacesByIdDocumentsBulkResponses = { - 202: BulkDocumentUploadAccepted -} - -export type PostKnowledgeSpacesByIdDocumentsBulkResponse = - PostKnowledgeSpacesByIdDocumentsBulkResponses[keyof PostKnowledgeSpacesByIdDocumentsBulkResponses] - -export type PostKnowledgeSpacesByIdDocumentsBulkReindexData = { - body: { - all?: boolean - documentIds?: Array - } - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/bulk/reindex' -} - -export type PostKnowledgeSpacesByIdDocumentsBulkReindexErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 413: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PostKnowledgeSpacesByIdDocumentsBulkReindexError = - PostKnowledgeSpacesByIdDocumentsBulkReindexErrors[keyof PostKnowledgeSpacesByIdDocumentsBulkReindexErrors] - -export type PostKnowledgeSpacesByIdDocumentsBulkReindexResponses = { - 202: BulkDocumentReindexResult -} - -export type PostKnowledgeSpacesByIdDocumentsBulkReindexResponse = - PostKnowledgeSpacesByIdDocumentsBulkReindexResponses[keyof PostKnowledgeSpacesByIdDocumentsBulkReindexResponses] - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdData = { - body: { - expectedRevision: number - } - headers: { - 'idempotency-key': string - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}' -} - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdError = - DeleteKnowledgeSpacesByIdDocumentsByDocumentIdErrors[keyof DeleteKnowledgeSpacesByIdDocumentsByDocumentIdErrors] - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdResponses = { - 202: DurableDeletionAccepted -} - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdResponse = - DeleteKnowledgeSpacesByIdDocumentsByDocumentIdResponses[keyof DeleteKnowledgeSpacesByIdDocumentsByDocumentIdResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdResponses = { - 200: DocumentAsset -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdResponses] - -export type GetKnowledgeSpacesByIdLogicalDocumentsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/logical-documents' -} - -export type GetKnowledgeSpacesByIdLogicalDocumentsErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdLogicalDocumentsError = - GetKnowledgeSpacesByIdLogicalDocumentsErrors[keyof GetKnowledgeSpacesByIdLogicalDocumentsErrors] - -export type GetKnowledgeSpacesByIdLogicalDocumentsResponses = { - 200: LogicalDocumentList -} - -export type GetKnowledgeSpacesByIdLogicalDocumentsResponse = - GetKnowledgeSpacesByIdLogicalDocumentsResponses[keyof GetKnowledgeSpacesByIdLogicalDocumentsResponses] - -export type DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdData = { - body: { - expectedRevision: number - } - headers: { - 'idempotency-key': string - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/logical-documents/{documentId}' -} - -export type DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdError = - DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdErrors[keyof DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdErrors] - -export type DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponses = { - 202: DurableDeletionAccepted -} - -export type DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponse = - DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponses[keyof DeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponses] - -export type GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/logical-documents/{documentId}' -} - -export type GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdError = - GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdErrors[keyof GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdErrors] - -export type GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponses = { - 200: LogicalDocument -} - -export type GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponse = - GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponses[keyof GetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/outline' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineResponses = { - 200: DocumentOutline -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsResponses = { - 200: DocumentRevisionList -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsResponses] - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackData = { - body: { - expectedActiveRevision: number - expectedRowVersion: number - } - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - revision: number - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions/{revision}/rollback' -} - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackError = - PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackErrors[keyof PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackErrors] - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackResponses = { - 202: DocumentProcessingTask -} - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackResponse = - PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackResponses[keyof PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackResponses] - -export type PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataData = { - body: { - expectedRowVersion: number - patch: { - [key: string]: unknown - } - } - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/metadata' -} - -export type PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataError = - PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataErrors[keyof PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataErrors] - -export type PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataResponses = { - 200: LogicalDocument -} - -export type PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataResponse = - PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataResponses[keyof PatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - revision: number - } - query?: { - cursor?: string - limit?: number - query?: string - } - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions/{revision}/chunks' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksResponses = { - 200: DocumentChunkList -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - revision: number - chunkId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions/{revision}/chunks/{chunkId}' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdResponses = - { - 200: DocumentRevisionChunk - } - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdResponses] - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateData = - { - body: { - enabled: boolean - } - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - revision: number - chunkId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/revisions/{revision}/chunks/{chunkId}/state' - } - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateErrors = - { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse - } - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateError = - PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateErrors[keyof PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateErrors] - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateResponses = - { - 202: DocumentChunkStateChangeAccepted - } - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateResponse = - PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateResponses[keyof PostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateResponses] - -export type GetKnowledgeSpacesByIdProcessingTasksData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/processing-tasks' -} - -export type GetKnowledgeSpacesByIdProcessingTasksErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdProcessingTasksError = - GetKnowledgeSpacesByIdProcessingTasksErrors[keyof GetKnowledgeSpacesByIdProcessingTasksErrors] - -export type GetKnowledgeSpacesByIdProcessingTasksResponses = { - 200: DocumentProcessingTaskList -} - -export type GetKnowledgeSpacesByIdProcessingTasksResponse = - GetKnowledgeSpacesByIdProcessingTasksResponses[keyof GetKnowledgeSpacesByIdProcessingTasksResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: { - cursor?: string - limit?: number - } - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksErrors = { - 400: ErrorResponse - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksResponses = { - 200: DocumentProcessingTaskList -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksResponses] - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - taskId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks/{taskId}' -} - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdError = - DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdErrors[keyof DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdErrors] - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponses = { - 200: DocumentProcessingTask -} - -export type DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponse = - DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponses[keyof DeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - taskId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks/{taskId}' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponses = { - 200: DocumentProcessingTask -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsData = { - body?: never - headers?: { - 'last-event-id'?: string - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - taskId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks/{taskId}/events' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsResponses = { - 200: DocumentProcessingTaskEvent -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsResponses] - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - taskId: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/processing-tasks/{taskId}/retry' -} - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError -} - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryError = - PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryErrors[keyof PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryErrors] - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryResponses = { - 200: DocumentProcessingTask -} - -export type PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryResponse = - PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryResponses[keyof PostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryResponses] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/settings' -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsError = - GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsErrors[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsErrors] - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponses = { - 200: DocumentSettingsHead -} - -export type GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponse = - GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponses[keyof GetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponses] - -export type PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsData = { - body: { - expectedSettingsHeadRevision: number | null - settings: { - chunkOverlap: number - chunkSize: number - enableGraph: boolean - enablePageIndex: boolean - language?: string - } - } - headers?: { - 'x-trace-id'?: string - } - path: { - documentId: string - id: string - } - query?: never - url: '/knowledge-fs/knowledge-spaces/{id}/documents/{documentId}/settings' -} - -export type PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsError = - PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsErrors[keyof PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsErrors] - -export type PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponses = { - 202: DocumentReindexAccepted -} - -export type PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponse = - PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponses[keyof PutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponses] - -export type DeleteJobsByIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/jobs/{id}' -} - -export type DeleteJobsByIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type DeleteJobsByIdError = DeleteJobsByIdErrors[keyof DeleteJobsByIdErrors] - -export type DeleteJobsByIdResponses = { - 200: DocumentCompilationJob -} - -export type DeleteJobsByIdResponse = DeleteJobsByIdResponses[keyof DeleteJobsByIdResponses] - -export type GetJobsByIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/jobs/{id}' -} - -export type GetJobsByIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type GetJobsByIdError = GetJobsByIdErrors[keyof GetJobsByIdErrors] - -export type GetJobsByIdResponses = { - 200: DocumentCompilationJob -} - -export type GetJobsByIdResponse = GetJobsByIdResponses[keyof GetJobsByIdResponses] - -export type PostJobsByIdRetryData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/jobs/{id}/retry' -} - -export type PostJobsByIdRetryErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PostJobsByIdRetryError = PostJobsByIdRetryErrors[keyof PostJobsByIdRetryErrors] - -export type PostJobsByIdRetryResponses = { - 200: DocumentCompilationJob -} - -export type PostJobsByIdRetryResponse = PostJobsByIdRetryResponses[keyof PostJobsByIdRetryResponses] - -export type GetDeletionJobsByJobIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - jobId: string - } - query?: never - url: '/knowledge-fs/deletion-jobs/{jobId}' -} - -export type GetDeletionJobsByJobIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError -} - -export type GetDeletionJobsByJobIdError = - GetDeletionJobsByJobIdErrors[keyof GetDeletionJobsByJobIdErrors] - -export type GetDeletionJobsByJobIdResponses = { - 200: DurableDeletionJob -} - -export type GetDeletionJobsByJobIdResponse = - GetDeletionJobsByJobIdResponses[keyof GetDeletionJobsByJobIdResponses] - -export type PostDeletionJobsByJobIdRetryData = { - body?: never - headers: { - 'idempotency-key': string - 'x-trace-id'?: string - } - path: { - jobId: string - } - query?: never - url: '/knowledge-fs/deletion-jobs/{jobId}/retry' -} - -export type PostDeletionJobsByJobIdRetryErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 409: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type PostDeletionJobsByJobIdRetryError = - PostDeletionJobsByJobIdRetryErrors[keyof PostDeletionJobsByJobIdRetryErrors] - -export type PostDeletionJobsByJobIdRetryResponses = { - 202: DurableDeletionAccepted -} - -export type PostDeletionJobsByJobIdRetryResponse = - PostDeletionJobsByJobIdRetryResponses[keyof PostDeletionJobsByJobIdRetryResponses] - -export type GetBulkJobsByIdData = { - body?: never - headers?: { - 'x-trace-id'?: string - } - path: { - id: string - } - query?: never - url: '/knowledge-fs/bulk-jobs/{id}' -} - -export type GetBulkJobsByIdErrors = { - 403: ConsoleProxyError - 404: ErrorResponse - 502: ConsoleProxyError - 503: ErrorResponse -} - -export type GetBulkJobsByIdError = GetBulkJobsByIdErrors[keyof GetBulkJobsByIdErrors] - -export type GetBulkJobsByIdResponses = { - 200: BulkOperationProgress -} - -export type GetBulkJobsByIdResponse = GetBulkJobsByIdResponses[keyof GetBulkJobsByIdResponses] diff --git a/packages/contracts/generated/knowledge-fs/zod.gen.ts b/packages/contracts/generated/knowledge-fs/zod.gen.ts deleted file mode 100644 index 359304ad6b1..00000000000 --- a/packages/contracts/generated/knowledge-fs/zod.gen.ts +++ /dev/null @@ -1,2257 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import * as z from 'zod' - -export const zKnowledgeSpaceCreationResponse = z.object({ - createdAt: z.iso.datetime(), - description: z.string().max(2000).optional(), - iconRef: z - .string() - .max(72) - .regex(/^builtin:[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/) - .optional(), - id: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/), - name: z.string().min(1).max(160), - revision: z.int().gt(0), - slug: z - .string() - .max(160) - .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/), - tenantId: z.string().min(1).max(255), - updatedAt: z.iso.datetime(), - configurationStatus: z.enum([ - 'pending-validation', - 'ready', - 'setup-required', - 'validation-failed', - ]), -}) - -export const zErrorResponse = z.object({ - code: z.string().optional(), - error: z.string(), -}) - -export const zCreateKnowledgeSpace = z.object({ - description: z.string().max(2000).optional(), - embeddingProfile: z - .object({ - model: z.string().min(1).max(256), - pluginId: z.string().min(1).max(256), - provider: z.string().min(1).max(256), - }) - .optional(), - iconRef: z - .string() - .max(72) - .regex(/^builtin:[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/) - .optional(), - idempotencyKey: z.string().min(1).max(255).optional(), - name: z.string().min(1).max(160), - retrievalProfile: z - .object({ - defaultMode: z.enum(['fast', 'research', 'deep']), - reasoningModel: z.object({ - model: z.string().min(1).max(256), - pluginId: z.string().min(1).max(256), - provider: z.string().min(1).max(256), - }), - rerank: z.object({ - enabled: z.boolean(), - model: z - .object({ - model: z.string().min(1).max(256), - pluginId: z.string().min(1).max(256), - provider: z.string().min(1).max(256), - }) - .optional(), - }), - scoreThreshold: z.object({ - enabled: z.boolean(), - stage: z.enum(['mode-final', 'rerank']), - value: z.number().gte(0).lte(1).optional(), - }), - topK: z.int().gte(1).lte(100), - }) - .optional(), - slug: z - .string() - .max(160) - .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/) - .optional(), -}) - -export const zKnowledgeSpace = z.object({ - createdAt: z.iso.datetime(), - description: z.string().max(2000).optional(), - iconRef: z - .string() - .max(72) - .regex(/^builtin:[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/) - .optional(), - id: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/), - name: z.string().min(1).max(160), - revision: z.int().gt(0), - slug: z - .string() - .max(160) - .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/), - tenantId: z.string().min(1).max(255), - updatedAt: z.iso.datetime(), -}) - -export const zKnowledgeSpaceList = z.object({ - items: z.array(zKnowledgeSpace), - nextCursor: z.string().optional(), -}) - -export const zKnowledgeSpaceStats = z.object({ - cache: z.object({ - available: z.boolean(), - entries: z.int().gte(0), - totalBytes: z.int().gte(0), - }), - commits: z.object({ - failedRetryable: z.int().gte(0), - failedTerminal: z.int().gte(0), - sampled: z.int().gte(0), - truncated: z.boolean(), - }), - generatedAt: z.iso.datetime(), - knowledgeSpaceId: z.uuid(), - metrics: z.object({ - available: z.boolean(), - reason: z.string().optional(), - }), - projections: z.object({ - denseVector: z.object({ - building: z.int().gte(0), - failed: z.int().gte(0), - ready: z.int().gte(0), - stale: z.int().gte(0), - total: z.int().gte(0), - }), - fts: z.object({ - building: z.int().gte(0), - failed: z.int().gte(0), - ready: z.int().gte(0), - stale: z.int().gte(0), - total: z.int().gte(0), - }), - graph: z.object({ - building: z.int().gte(0), - failed: z.int().gte(0), - ready: z.int().gte(0), - stale: z.int().gte(0), - total: z.int().gte(0), - }), - metadata: z.object({ - building: z.int().gte(0), - failed: z.int().gte(0), - ready: z.int().gte(0), - stale: z.int().gte(0), - total: z.int().gte(0), - }), - projectionVersion: z.int().gt(0), - }), - runtime: z.object({ - activeLeaseSampleCount: z.int().gte(0), - activeSessionSampleCount: z.int().gte(0), - truncated: z.boolean(), - }), - storage: z.object({ - documentCount: z.int().gte(0), - rawDocumentBytes: z.int().gte(0), - }), - tenantId: z.string(), - window: z.object({ - end: z.iso.datetime(), - minutes: z.int().gt(0).lte(1440), - start: z.iso.datetime(), - }), -}) - -export const zDurableDeletionJob = z.object({ - checkpoint: z.enum([ - 'requested', - 'quiescing', - 'deleting_objects', - 'deleting_derived_data', - 'deleting_primary_data', - 'completed', - ]), - completedAt: z.iso.datetime().optional(), - createdAt: z.iso.datetime(), - error: z - .object({ - code: z.string().regex(/^[A-Z][A-Z0-9_]{0,63}$/), - message: z.string().min(1).max(256), - retryable: z.boolean(), - }) - .optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - mode: z.enum(['cascade', 'keep']).optional(), - progress: z - .object({ - completedItems: z.int().gte(0), - currentItemKind: z.string().min(1).optional(), - totalItems: z.int().gte(0).optional(), - }) - .optional(), - retryAt: z.iso.datetime().optional(), - runState: z.enum([ - 'dispatch_pending', - 'queued', - 'running', - 'retry_wait', - 'completed', - 'failed', - 'canceled', - ]), - targetId: z.uuid(), - targetType: z.enum(['knowledge_space', 'source', 'document', 'logical_document']), - updatedAt: z.iso.datetime(), -}) - -export const zDurableDeletionAccepted = z.object({ - job: zDurableDeletionJob, - statusUrl: z.string().min(1), -}) - -export const zDurableBulkDeletionAccepted = z.object({ - items: z.array( - z.object({ - documentId: z.uuid(), - job: zDurableDeletionJob, - statusUrl: z.string().min(1), - }), - ), - total: z.int().gt(0), -}) - -export const zDocumentAsset = z.object({ - createdAt: z.iso.datetime(), - filename: z.string().min(1).max(512), - id: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/), - knowledgeSpaceId: z - .string() - .regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/), - metadata: z.record(z.string(), z.unknown()).optional().default({}), - mimeType: z.string().min(1), - objectKey: z.string().min(1), - parserStatus: z.enum(['pending', 'parsed', 'failed']), - sha256: z.string().regex(/^[0-9a-f]{64}$/), - sizeBytes: z.int().gte(0), - sourceId: z - .string() - .regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/) - .optional(), - updatedAt: z.iso.datetime().optional(), - version: z.int().gt(0), -}) - -export const zDocumentAssetList = z.object({ - items: z.array(zDocumentAsset), - nextCursor: z.uuid().optional(), -}) - -export const zDocumentOutlineNode = z.object({ - childNodeIds: z.array(z.string()).optional().default([]), - children: z.array(z.record(z.string(), z.unknown())).optional().default([]), - endOffset: z.int().gte(0).optional(), - endPage: z.int().gt(0).optional(), - id: z.string(), - level: z.int().gt(0), - metadata: z.record(z.string(), z.unknown()), - sectionPath: z.array(z.string()).optional().default([]), - sourceElementIds: z.array(z.string()).optional().default([]), - sourceNodeIds: z.array(z.string()).optional().default([]), - startOffset: z.int().gte(0).optional(), - startPage: z.int().gt(0).optional(), - summary: z.string().optional(), - title: z.string(), - titleLocation: z.record(z.string(), z.unknown()).optional(), - tocSource: z.string(), -}) - -export const zDocumentOutline = z.object({ - artifactHash: z.string(), - createdAt: z.string(), - documentAssetId: z.uuid(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - metadata: z.record(z.string(), z.unknown()), - nodes: z.array(zDocumentOutlineNode), - outlineVersion: z.string(), - parseArtifactId: z.uuid(), - updatedAt: z.string().optional(), - version: z.int().gt(0), -}) - -export const zLogicalDocumentRevision = z - .object({ - activatedAt: z.string().optional(), - contentHash: z.string().length(64), - createdAt: z.string(), - documentAssetId: z.uuid(), - documentAssetVersion: z.int().gt(0), - documentId: z.uuid(), - knowledgeSpaceId: z.uuid(), - mimeType: z.string(), - revision: z.int().gt(0), - sizeBytes: z.int().gte(0), - state: z.enum(['candidate', 'active', 'superseded', 'failed']), - }) - .nullable() - -export const zLogicalDocument = z.object({ - active: zLogicalDocumentRevision, - activeRevision: z.int().gt(0).optional(), - createdAt: z.string(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - providerItemId: z.string().optional(), - rowVersion: z.int().gte(0), - sourceId: z.uuid().optional(), - status: z.enum(['pending', 'ready', 'failed', 'deleting']), - title: z.string(), - updatedAt: z.string(), - userMetadata: z.record(z.string(), z.unknown()), -}) - -export const zLogicalDocumentList = z.object({ - items: z.array(zLogicalDocument), - nextCursor: z.string().optional(), -}) - -export const zDocumentRevisionList = z.object({ - items: z.array(zLogicalDocumentRevision.and(z.record(z.string(), z.unknown()))), - nextCursor: z.string().optional(), -}) - -export const zDocumentProcessingTask = z.object({ - completedAt: z.string().optional(), - createdAt: z.string(), - documentId: z.uuid(), - documentRevision: z.int().gt(0), - errorCode: z.string().optional(), - errorMessage: z.string().optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - progressPercent: z.int().gte(0).lte(100), - retryAt: z.string().optional(), - stage: z.enum([ - 'queued', - 'parsed', - 'outline_built', - 'nodes_generated', - 'projection_built', - 'smoke_eval_passed', - 'published', - ]), - state: z.enum([ - 'dispatch_pending', - 'queued', - 'running', - 'retry_wait', - 'succeeded', - 'failed', - 'canceled', - 'superseded', - ]), - updatedAt: z.string(), -}) - -export const zDocumentRevisionChunk = z.object({ - createdAt: z.string(), - documentId: z.uuid(), - documentRevision: z.int().gt(0), - enabled: z.boolean(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - ordinal: z.int().gte(0), - parentChunkId: z.uuid().optional(), - text: z.string(), - tokenCount: z.int().gte(0), - userMetadata: z.record(z.string(), z.unknown()), -}) - -export const zDocumentChunkList = z.object({ - items: z.array(zDocumentRevisionChunk), - nextCursor: z.string().optional(), -}) - -export const zDocumentChunkStateChangeAccepted = z.object({ - candidateFingerprint: z.string().optional(), - candidatePublicationId: z.uuid().optional(), - chunkId: z.uuid(), - compilationAttemptId: z.uuid(), - createdAt: z.string(), - documentId: z.uuid(), - documentRevision: z.int().gt(0), - enabled: z.boolean(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - state: z.enum(['candidate']), - statusUrl: z.string().min(1), -}) - -export const zDocumentProcessingTaskList = z.object({ - items: z.array(zDocumentProcessingTask), - nextCursor: z.string().optional(), -}) - -export const zDocumentProcessingTaskEvent = z.union([ - z.object({ - data: z.object({ - progressPercent: z.int().gte(0).lte(100), - stage: z.enum([ - 'queued', - 'parsed', - 'outline_built', - 'nodes_generated', - 'projection_built', - 'smoke_eval_passed', - 'published', - ]), - state: z.enum([ - 'dispatch_pending', - 'queued', - 'running', - 'retry_wait', - 'succeeded', - 'failed', - 'canceled', - 'superseded', - ]), - updatedAt: z.string(), - }), - event: z.enum(['progress']), - }), - z.object({ - data: z.object({ - errorCode: z.string().optional(), - state: z.enum(['succeeded', 'failed', 'canceled', 'superseded']), - }), - event: z.enum(['terminal']), - }), -]) - -export const zDocumentSettingsHead = z.object({ - activeRevision: z.int().gt(0), - profile: z.object({ - activatedAt: z.string().optional(), - createdAt: z.string(), - revision: z.int().gt(0), - settings: z.object({ - chunkOverlap: z.int().gte(0).lte(8191), - chunkSize: z.int().gte(128).lte(8192), - enableGraph: z.boolean(), - enablePageIndex: z.boolean(), - language: z.string().min(2).max(64).optional(), - }), - state: z.enum(['active']), - }), - rowVersion: z.int().gte(0), - updatedAt: z.string(), -}) - -export const zDocumentReindexAccepted = z.object({ - attemptId: z.uuid(), - compilationAttemptId: z.uuid(), - settingsRevision: z.int().gt(0), - state: z.enum(['running']), - statusUrl: z.string(), -}) - -export const zDocumentCompilationJob = z.object({ - baseHeadRevision: z.int().gte(0).optional(), - candidateFingerprint: z.string().min(1).optional(), - candidatePublicationId: z.uuid().optional(), - completedAt: z.number().optional(), - createdAt: z.number(), - documentAssetId: z.string().min(1), - error: z.string().optional(), - executionAttempts: z.int().gte(0).optional(), - id: z.string().min(1), - knowledgeSpaceId: z.string().min(1), - leaseExpiresAt: z.number().optional(), - maxExecutionAttempts: z.int().gt(0).optional(), - publicationGenerationId: z - .string() - .regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/) - .optional(), - queueJobId: z.string().min(1).optional(), - retryAt: z.number().optional(), - runState: z - .enum([ - 'dispatch_pending', - 'queued', - 'running', - 'retry_wait', - 'succeeded', - 'failed', - 'canceled', - 'superseded', - ]) - .optional(), - stage: z.enum([ - 'queued', - 'parsed', - 'outline_built', - 'nodes_generated', - 'projection_built', - 'smoke_eval_passed', - 'published', - 'failed', - 'canceled', - ]), - tenantId: z.string().min(1).max(255), - updatedAt: z.number(), - version: z.int().gt(0), -}) - -export const zBulkOperationProgress = z.object({ - completedItems: z.int().gte(0), - createdAt: z.string(), - failedItemIds: z.array(z.string().min(1)), - failedItems: z.int().gte(0), - id: z.string().min(1), - knowledgeSpaceId: z.string().min(1), - status: z.enum(['running', 'completed', 'failed']), - totalItems: z.int().gte(0), - type: z.enum(['document_upload', 'document_delete', 'document_reindex']), - updatedAt: z.string(), -}) - -export const zBulkDocumentReindexResult = z.object({ - bulkJobId: z.string().min(1), - items: z.array( - z.union([ - z.object({ - asset: zDocumentAsset, - compilationJob: z.object({ - id: z.string().min(1), - stage: z.enum(['queued']), - }), - status: z.enum(['queued']), - statusUrl: z.string().min(1), - }), - z.object({ - documentId: z.uuid(), - status: z.enum(['not_found']), - }), - ]), - ), - total: z.int().gte(0), -}) - -export const zDocumentUploadAccepted = z.object({ - asset: zDocumentAsset, - assetStatusUrl: z.string().min(1).optional(), - compilationJob: z.object({ - id: z.string().min(1), - stage: z.enum(['queued']), - }), - logicalDocument: z.object({ - id: z.uuid(), - revision: z.int().gt(0), - }), - logicalDocumentId: z.uuid(), - documentRevision: z.int().gt(0), - statusUrl: z.string().min(1), - status: z.enum(['accepted']).optional(), -}) - -export const zBulkDocumentUploadAccepted = z.object({ - accepted: z.int().gte(0), - bulkJobId: z.string().min(1), - excluded: z.int().gte(0), - items: z.array( - z.union([ - zDocumentUploadAccepted, - z.object({ - filename: z.string(), - index: z.int().gte(0), - mimeType: z.string(), - reason: z.enum([ - 'batch_byte_limit_exceeded', - 'document_not_found', - 'file_count_limit_exceeded', - 'file_too_large', - 'invalid_file', - 'invalid_target', - 'processing_failed', - 'quota_exceeded', - 'revision_conflict', - 'unsupported_mime_type', - ]), - sizeBytes: z.int().gte(0), - status: z.enum(['excluded']), - }), - ]), - ), - total: z.int().gte(0), -}) - -export const zSourceWorkflowRun = z.object({ - canceledAt: z.string().optional(), - checkpoint: z.string(), - completedAt: z.string().optional(), - createdAt: z.string(), - cursor: z.string().optional(), - executionAttempts: z.int(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - kind: z.string(), - lastErrorCode: z.string().optional(), - maxExecutionAttempts: z.int(), - progressCompleted: z.int(), - progressFailed: z.int(), - progressSkipped: z.int(), - progressTotal: z.int().optional(), - sourceId: z.uuid().optional(), - state: z.string(), - updatedAt: z.string(), -}) - -export const zSource = z.object({ - connectionId: z - .string() - .regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/) - .optional(), - createdAt: z.iso.datetime(), - id: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/), - knowledgeSpaceId: z - .string() - .regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/), - metadata: z.record(z.string(), z.unknown()), - name: z.string().min(1).max(200), - permissionScope: z.array(z.string().min(1)).optional().default([]), - status: z.enum(['active', 'syncing', 'error', 'disabled']), - type: z.enum(['upload', 'object-storage', 'connector', 'web']), - updatedAt: z.iso.datetime(), - uri: z.string().min(1), - version: z.int().gte(1).optional().default(1), - credentialConfigured: z.boolean().optional(), -}) - -export const zWebsiteCrawlResult = z.object({ - completed: z.number().optional(), - failed: z.number().optional(), - imported: z.number().optional(), - pages: z.array( - z.object({ - content: z.string(), - description: z.string().optional(), - sourceUrl: z.string(), - title: z.string().optional(), - }), - ), - replaced: z.number().optional(), - skipped: z.number().optional(), - status: z.string().optional(), - total: z.number().optional(), -}) - -export const zOnlineDocumentPages = z.object({ - nextCursor: z.string().optional(), - workspaces: z.array( - z.object({ - pages: z.array( - z.object({ - lastEditedTime: z.string().optional(), - pageId: z.string(), - pageName: z.string(), - parentId: z.string().optional(), - type: z.string(), - }), - ), - total: z.number().optional(), - workspaceId: z.string().optional(), - workspaceName: z.string().optional(), - }), - ), -}) - -export const zSourceImportResult = z.object({ - documents: z.array( - z.object({ - documentAssetId: z.string(), - filename: z.string(), - }), - ), - failed: z.array( - z.object({ - code: z.string(), - error: z.string(), - filename: z.string(), - }), - ), - skipped: z.array(z.string()), -}) - -export const zSourceCredentialTest = z.object({ - code: z.string().optional(), - error: z.string().optional(), - valid: z.boolean(), -}) - -export const zOnlineDriveFiles = z.object({ - buckets: z.array( - z.object({ - bucket: z.string().optional(), - continuationToken: z.string().optional(), - files: z.array( - z.object({ - id: z.string(), - name: z.string(), - size: z.number().optional(), - type: z.string(), - }), - ), - isTruncated: z.boolean().optional(), - }), - ), -}) - -export const zConsoleProxyError = z.object({ - code: z.string(), - message: z.string(), - status: z.int(), -}) - -export const zListKnowledgeSpacesHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zListKnowledgeSpacesQuery = z.object({ - cursor: z.string().optional(), - limit: z.int().gte(1).lte(100).optional().default(100), -}) - -/** - * Tenant knowledge spaces - */ -export const zListKnowledgeSpacesResponse = zKnowledgeSpaceList - -export const zCreateKnowledgeSpaceBody = zCreateKnowledgeSpace - -export const zCreateKnowledgeSpaceHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -/** - * Created knowledge space - */ -export const zCreateKnowledgeSpaceResponse = zKnowledgeSpaceCreationResponse - -export const zDeleteKnowledgeSpacesByIdBody = z.object({ - challenge: z.string().min(1).max(160), - expectedRevision: z.int().gt(0), -}) - -export const zDeleteKnowledgeSpacesByIdHeaders = z.object({ - 'idempotency-key': z.string().min(8).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zDeleteKnowledgeSpacesByIdPath = z.object({ - id: z.uuid(), -}) - -/** - * Durable deletion accepted - */ -export const zDeleteKnowledgeSpacesByIdResponse = zDurableDeletionAccepted - -export const zGetKnowledgeSpacesByIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdPath = z.object({ - id: z.uuid(), -}) - -/** - * Knowledge space - */ -export const zGetKnowledgeSpacesByIdResponse = zKnowledgeSpace - -export const zPatchKnowledgeSpacesByIdBody = z.object({ - description: z.string().max(2000).optional(), - expectedRevision: z.int().gt(0), - iconRef: z - .string() - .max(72) - .regex(/^builtin:[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/) - .nullish(), - name: z.string().min(1).max(160).optional(), - slug: z - .string() - .max(160) - .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/) - .optional(), -}) - -export const zPatchKnowledgeSpacesByIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPatchKnowledgeSpacesByIdPath = z.object({ - id: z.uuid(), -}) - -/** - * Updated knowledge space - */ -export const zPatchKnowledgeSpacesByIdResponse = zKnowledgeSpace - -export const zGetKnowledgeSpacesByIdStatsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdStatsPath = z.object({ - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdStatsQuery = z.object({ - windowMinutes: z.int().gte(1).lte(1440).optional(), -}) - -/** - * Low-cardinality KnowledgeSpace statistics - */ -export const zGetKnowledgeSpacesByIdStatsResponse = zKnowledgeSpaceStats - -export const zGetKnowledgeSpacesByIdAccessPolicyHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdAccessPolicyPath = z.object({ - id: z.uuid(), -}) - -/** - * Knowledge space visibility policy - */ -export const zGetKnowledgeSpacesByIdAccessPolicyResponse = z.object({ - id: z.string().min(1), - ownerSubjectId: z.string().min(1).max(255), - partialMemberSubjectIds: z.array(z.string().min(1).max(255)), - revision: z.int().gt(0), - visibility: z.enum(['only_me', 'all_members', 'partial_members']), -}) - -export const zPatchKnowledgeSpacesByIdAccessPolicyBody = z.object({ - expectedRevision: z.int().gt(0), - partialMemberSubjectIds: z.array(z.string().min(1).max(255)).max(500).optional().default([]), - visibility: z.enum(['only_me', 'all_members', 'partial_members']), -}) - -export const zPatchKnowledgeSpacesByIdAccessPolicyHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPatchKnowledgeSpacesByIdAccessPolicyPath = z.object({ - id: z.uuid(), -}) - -/** - * Updated knowledge space visibility policy - */ -export const zPatchKnowledgeSpacesByIdAccessPolicyResponse = z.object({ - id: z.string().min(1), - ownerSubjectId: z.string().min(1).max(255), - partialMemberSubjectIds: z.array(z.string().min(1).max(255)), - revision: z.int().gt(0), - visibility: z.enum(['only_me', 'all_members', 'partial_members']), -}) - -export const zGetSourceProvidersHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -/** - * Source provider capability catalog - */ -export const zGetSourceProvidersResponse = z.object({ - items: z.array( - z.object({ - authKinds: z.array(z.enum(['api-key', 'endpoint', 'oauth2'])), - available: z.boolean(), - capabilities: z.array(z.enum(['website-crawl', 'online-document', 'online-drive'])), - configuration: z.array( - z.object({ - description: z.string().optional(), - format: z.enum(['password', 'uri']).optional(), - name: z.string(), - required: z.boolean(), - secret: z.boolean(), - type: z.enum(['boolean', 'integer', 'string']), - }), - ), - displayName: z.string(), - id: z.string(), - unavailableReason: z.string().optional(), - }), - ), -}) - -export const zGetKnowledgeSpacesByIdSourceConnectionsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourceConnectionsPath = z.object({ - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdSourceConnectionsQuery = z.object({ - cursor: z.string().max(4096).optional(), - limit: z.int().gte(1).lte(200).optional().default(50), -}) - -/** - * Source connections - */ -export const zGetKnowledgeSpacesByIdSourceConnectionsResponse = z.object({ - items: z.array( - z.object({ - authKind: z.enum(['api-key', 'endpoint', 'oauth2']), - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])), - createdAt: z.string(), - errorCode: z.string().optional(), - expiresAt: z.string().optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - name: z.string(), - providerId: z.string(), - scopes: z.array(z.string()), - status: z.enum(['provisioning', 'active', 'expired', 'error', 'revoked']), - updatedAt: z.string(), - version: z.int(), - }), - ), - nextCursor: z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsBody = z.object({ - authKind: z.enum(['api-key', 'endpoint']), - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])).optional(), - credentials: z.record(z.string(), z.unknown()), - name: z.string().min(1).max(160), - providerId: z.string().min(1).max(128), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsPath = z.object({ - id: z.uuid(), -}) - -/** - * Source connection created - */ -export const zPostKnowledgeSpacesByIdSourceConnectionsResponse = z.object({ - authKind: z.enum(['api-key', 'endpoint', 'oauth2']), - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])), - createdAt: z.string(), - errorCode: z.string().optional(), - expiresAt: z.string().optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - name: z.string(), - providerId: z.string(), - scopes: z.array(z.string()), - status: z.enum(['provisioning', 'active', 'expired', 'error', 'revoked']), - updatedAt: z.string(), - version: z.int(), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsOauthBody = z.object({ - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])).optional(), - name: z.string().min(1).max(160), - providerId: z.string().min(1).max(128), - redirectUri: z.string().min(1).max(2048), - scopes: z.array(z.string().min(1).max(255)).max(100).optional().default([]), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsOauthHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsOauthPath = z.object({ - id: z.uuid(), -}) - -/** - * OAuth authorization started - */ -export const zPostKnowledgeSpacesByIdSourceConnectionsOauthResponse = z.object({ - authorizationUrl: z.string(), - connection: z.object({ - authKind: z.enum(['api-key', 'endpoint', 'oauth2']), - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])), - createdAt: z.string(), - errorCode: z.string().optional(), - expiresAt: z.string().optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - name: z.string(), - providerId: z.string(), - scopes: z.array(z.string()), - status: z.enum(['provisioning', 'active', 'expired', 'error', 'revoked']), - updatedAt: z.string(), - version: z.int(), - }), -}) - -export const zPostSourceOauthCallbackBody = z.object({ - code: z.string().min(1).max(8192), - state: z.string().min(32).max(256), -}) - -export const zPostSourceOauthCallbackHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -/** - * OAuth connection activated - */ -export const zPostSourceOauthCallbackResponse = z.object({ - authKind: z.enum(['api-key', 'endpoint', 'oauth2']), - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])), - createdAt: z.string(), - errorCode: z.string().optional(), - expiresAt: z.string().optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - name: z.string(), - providerId: z.string(), - scopes: z.array(z.string()), - status: z.enum(['provisioning', 'active', 'expired', 'error', 'revoked']), - updatedAt: z.string(), - version: z.int(), -}) - -export const zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdPath = z.object({ - id: z.uuid(), - connectionId: z.uuid(), -}) - -export const zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdQuery = z.object({ - expectedVersion: z.int().gte(1), -}) - -/** - * Source connection locally revoked - */ -export const zDeleteKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponse = z.object({ - authKind: z.enum(['api-key', 'endpoint', 'oauth2']), - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])), - createdAt: z.string(), - errorCode: z.string().optional(), - expiresAt: z.string().optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - name: z.string(), - providerId: z.string(), - scopes: z.array(z.string()), - status: z.enum(['provisioning', 'active', 'expired', 'error', 'revoked']), - updatedAt: z.string(), - version: z.int(), -}) - -export const zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdPath = z.object({ - id: z.uuid(), - connectionId: z.uuid(), -}) - -/** - * Source connection - */ -export const zGetKnowledgeSpacesByIdSourceConnectionsByConnectionIdResponse = z.object({ - authKind: z.enum(['api-key', 'endpoint', 'oauth2']), - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])), - createdAt: z.string(), - errorCode: z.string().optional(), - expiresAt: z.string().optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - name: z.string(), - providerId: z.string(), - scopes: z.array(z.string()), - status: z.enum(['provisioning', 'active', 'expired', 'error', 'revoked']), - updatedAt: z.string(), - version: z.int(), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshBody = z.object({ - expectedVersion: z.int().gte(1), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshPath = z.object({ - id: z.uuid(), - connectionId: z.uuid(), -}) - -/** - * Source connection refreshed - */ -export const zPostKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefreshResponse = z.object({ - authKind: z.enum(['api-key', 'endpoint', 'oauth2']), - configuration: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])), - createdAt: z.string(), - errorCode: z.string().optional(), - expiresAt: z.string().optional(), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - name: z.string(), - providerId: z.string(), - scopes: z.array(z.string()), - status: z.enum(['provisioning', 'active', 'expired', 'error', 'revoked']), - updatedAt: z.string(), - version: z.int(), -}) - -export const zGetKnowledgeSpacesByIdSourcesHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourcesPath = z.object({ - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdSourcesQuery = z.object({ - cursor: z.string().optional(), - limit: z.int().gte(1).lte(200).optional(), -}) - -/** - * Knowledge space sources - */ -export const zGetKnowledgeSpacesByIdSourcesResponse = z.object({ - items: z.array(zSource), - nextCursor: z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBody = z.object({ - connectionId: z.uuid().optional(), - credentials: z.record(z.string(), z.unknown()).optional(), - metadata: z.record(z.string(), z.unknown()).optional(), - name: z.string().min(1).max(200), - permissionScope: z.array(z.string().min(1)).optional(), - status: z.enum(['active', 'syncing', 'error', 'disabled']).optional(), - type: z.enum(['upload', 'object-storage', 'connector', 'web']), - uri: z.string().min(1), -}) - -export const zPostKnowledgeSpacesByIdSourcesHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesPath = z.object({ - id: z.uuid(), -}) - -/** - * Created source - */ -export const zPostKnowledgeSpacesByIdSourcesResponse = zSource - -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdBody = z.object({ - expectedRevision: z.int().gt(0), -}) - -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdHeaders = z.object({ - 'idempotency-key': z.string().min(8).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdQuery = z.object({ - documents: z.enum(['cascade', 'keep']).optional().default('cascade'), -}) - -/** - * Durable deletion accepted - */ -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdResponse = zDurableDeletionAccepted - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Source - */ -export const zGetKnowledgeSpacesByIdSourcesBySourceIdResponse = zSource - -export const zPatchKnowledgeSpacesByIdSourcesBySourceIdBody = z.object({ - expectedVersion: z.int().gte(1).optional(), - metadata: z.record(z.string(), z.unknown()).optional(), - name: z.string().min(1).max(200).optional(), - status: z.enum(['active', 'syncing', 'error', 'disabled']).optional(), -}) - -export const zPatchKnowledgeSpacesByIdSourcesBySourceIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPatchKnowledgeSpacesByIdSourcesBySourceIdPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Updated source - */ -export const zPatchKnowledgeSpacesByIdSourcesBySourceIdResponse = zSource - -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsQuery = z.object({ - expectedVersion: z.int().gte(1), -}) - -/** - * Revoked source credentials - */ -export const zDeleteKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponse = zSource - -export const zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsBody = z.object({ - credentials: z.record(z.string(), z.unknown()), - expectedVersion: z.int().gte(1), -}) - -export const zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Rotated source credentials; secret bytes are returned neither here nor later - */ -export const zPutKnowledgeSpacesByIdSourcesBySourceIdCredentialsResponse = zSource - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdSyncHeaders = z.object({ - 'Idempotency-Key': z.string().min(1).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdSyncPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Durable source sync accepted - */ -export const zPostKnowledgeSpacesByIdSourcesBySourceIdSyncResponse = zSourceWorkflowRun - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewHeaders = z.object({ - 'Idempotency-Key': z.string().min(1).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Durable crawl preview accepted - */ -export const zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPreviewResponse = zSourceWorkflowRun - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsBody = z.union([ - z.object({ - items: z - .array( - z.object({ - etag: z.string().max(2048).optional(), - lastEditedTime: z.string().max(2048).optional(), - name: z.string().max(500).optional(), - pageId: z.string().min(1).max(2048), - providerItemId: z.string().min(1).max(2048), - type: z.string().min(1).max(128), - workspaceId: z.string().min(1).max(2048), - }), - ) - .min(1) - .max(200), - kind: z.enum(['online-document-import']), - }), - z.object({ - items: z - .array( - z.object({ - bucket: z.string().max(2048).optional(), - etag: z.string().max(2048).optional(), - id: z.string().min(1).max(2048), - mimeType: z.string().max(255).optional(), - name: z.string().min(1).max(500), - providerItemId: z.string().min(1).max(2048), - }), - ) - .min(1) - .max(200), - kind: z.enum(['online-drive-import']), - }), -]) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsHeaders = z.object({ - 'Idempotency-Key': z.string().min(1).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Durable provider import accepted - */ -export const zPostKnowledgeSpacesByIdSourcesBySourceIdWorkflowImportsResponse = zSourceWorkflowRun - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdPagesHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdPagesPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdPagesQuery = z.object({ - cursor: z.string().min(1).max(4096).optional(), - limit: z.int().gte(1).lte(200).optional().default(50), -}) - -/** - * Authorized online-document pages - */ -export const zGetKnowledgeSpacesByIdSourcesBySourceIdPagesResponse = zOnlineDocumentPages - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdFilesHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdFilesPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdFilesQuery = z.object({ - bucket: z.string().optional(), - continuationToken: z.string().min(1).max(4096).optional(), - maxKeys: z.int().gte(1).lte(1000).optional(), - prefix: z.string().optional(), -}) - -/** - * Online-drive files - */ -export const zGetKnowledgeSpacesByIdSourcesBySourceIdFilesResponse = zOnlineDriveFiles - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Website crawl result - */ -export const zPostKnowledgeSpacesByIdSourcesBySourceIdCrawlResponse = zWebsiteCrawlResult - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdImportBody = z.object({ - pages: z - .array( - z.object({ - lastEditedTime: z.string().min(1).optional(), - name: z.string().min(1).max(200).optional(), - pageId: z.string().min(1), - type: z.string().min(1), - workspaceId: z.string().min(1), - }), - ) - .min(1) - .max(200), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdImportHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdImportPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Imported online-document pages - */ -export const zPostKnowledgeSpacesByIdSourcesBySourceIdImportResponse = zSourceImportResult - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdTestHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdTestPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Source credential validation result - */ -export const zPostKnowledgeSpacesByIdSourcesBySourceIdTestResponse = zSourceCredentialTest - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesBody = z.object({ - files: z - .array( - z.object({ - bucket: z.string().optional(), - id: z.string().min(1), - mimeType: z.string().optional(), - name: z.string().min(1).max(255), - }), - ) - .min(1) - .max(200), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Imported online-drive files - */ -export const zPostKnowledgeSpacesByIdSourcesBySourceIdImportFilesResponse = zSourceImportResult - -export const zPostKnowledgeSpacesByIdSourcesBulkBody = z.object({ - action: z.enum(['sync', 'disable', 'remove']), - sourceIds: z.array(z.uuid()).min(1).max(200), -}) - -export const zPostKnowledgeSpacesByIdSourcesBulkHeaders = z.object({ - 'Idempotency-Key': z.string().min(1).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourcesBulkPath = z.object({ - id: z.uuid(), -}) - -/** - * Durable bulk source workflow accepted - */ -export const zPostKnowledgeSpacesByIdSourcesBulkResponse = zSourceWorkflowRun - -export const zGetKnowledgeSpacesByIdSourceWorkflowsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsPath = z.object({ - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsQuery = z.object({ - cursor: z.string().max(4096).optional(), - limit: z.int().gte(1).lte(200).optional().default(50), - sourceId: z.uuid().optional(), -}) - -/** - * Source workflow history - */ -export const zGetKnowledgeSpacesByIdSourceWorkflowsResponse = z.object({ - items: z.array(zSourceWorkflowRun), - nextCursor: z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPath = z.object({ - id: z.uuid(), - runId: z.uuid(), -}) - -/** - * Source workflow - */ -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdResponse = zSourceWorkflowRun - -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsPath = z.object({ - id: z.uuid(), - runId: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsQuery = z.object({ - cursor: z.string().max(4096).optional(), - limit: z.int().gte(1).lte(200).optional().default(50), -}) - -/** - * Per-source bulk workflow results - */ -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdBulkItemsResponse = z.object({ - items: z.array( - z.object({ - action: z.enum(['sync', 'disable', 'remove']), - errorCode: z.string().optional(), - id: z.uuid(), - reason: z.string().optional(), - sourceId: z.uuid(), - status: z.enum(['eligible', 'running', 'skipped', 'failed', 'completed']), - updatedAt: z.string(), - }), - ), - nextCursor: z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesPath = z.object({ - id: z.uuid(), - runId: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesQuery = z.object({ - cursor: z.string().max(4096).optional(), - limit: z.int().gte(1).lte(200).optional().default(50), -}) - -/** - * Crawl preview pages (content excluded) - */ -export const zGetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponse = z.object({ - items: z.array( - z.object({ - description: z.string().optional(), - etag: z.string().optional(), - pageId: z.string(), - sourceUrl: z.string(), - title: z.string().optional(), - }), - ), - nextCursor: z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelBody = z.object({ - reason: z.string().max(1000).optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelPath = z.object({ - id: z.uuid(), - runId: z.uuid(), -}) - -/** - * Source workflow canceled - */ -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdCancelResponse = zSourceWorkflowRun - -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryPath = z.object({ - id: z.uuid(), - runId: z.uuid(), -}) - -/** - * Source workflow retried - */ -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdRetryResponse = zSourceWorkflowRun - -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionBody = z.object({ - pageIds: z.array(z.string().min(1).max(128)).min(1).max(200), -}) - -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionHeaders = z.object({ - 'Idempotency-Key': z.string().min(1).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionPath = z.object({ - id: z.uuid(), - runId: z.uuid(), -}) - -/** - * Crawl import selection accepted - */ -export const zPostKnowledgeSpacesByIdSourceWorkflowsByRunIdSelectionResponse = zSourceWorkflowRun - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Source sync policy - */ -export const zGetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse = z.object({ - createdAt: z.string(), - customIntervalSeconds: z.int().optional(), - enabled: z.boolean(), - expectedSourceVersion: z.int().gte(1), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - mode: z.enum(['provider', 'manual', 'interval', 'custom']), - nextRunAt: z.string().optional(), - revision: z.int().gte(1), - sourceId: z.uuid(), - updatedAt: z.string(), -}) - -export const zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyBody = z.object({ - customIntervalSeconds: z.int().gte(3600).lte(2592000).optional(), - enabled: z.boolean(), - expectedRevision: z.int().gte(0), - expectedSourceVersion: z.int().gte(1), - mode: z.enum(['provider', 'manual', 'interval', 'custom']), -}) - -export const zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyPath = z.object({ - id: z.uuid(), - sourceId: z.uuid(), -}) - -/** - * Source sync policy updated - */ -export const zPutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse = z.object({ - createdAt: z.string(), - customIntervalSeconds: z.int().optional(), - enabled: z.boolean(), - expectedSourceVersion: z.int().gte(1), - id: z.uuid(), - knowledgeSpaceId: z.uuid(), - mode: z.enum(['provider', 'manual', 'interval', 'custom']), - nextRunAt: z.string().optional(), - revision: z.int().gte(1), - sourceId: z.uuid(), - updatedAt: z.string(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsPath = z.object({ - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsQuery = z.object({ - cursor: z.uuid().optional(), - limit: z.int().gte(1).lte(100).optional(), -}) - -/** - * Document assets - */ -export const zGetKnowledgeSpacesByIdDocumentsResponse = zDocumentAssetList - -export const zPostKnowledgeSpacesByIdDocumentsBody = z.object({ - documentId: z.uuid().optional(), - expectedActiveRevision: z.union([z.int().gt(0), z.enum(['null'])]).optional(), - expectedDocumentRowVersion: z.int().gte(0).nullish(), - file: z.custom(), - sourceId: z.uuid().optional(), -}) - -export const zPostKnowledgeSpacesByIdDocumentsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdDocumentsPath = z.object({ - id: z.uuid(), -}) - -export const zPostKnowledgeSpacesByIdDocumentsResponse = z.union([ - zDocumentAsset, - zDocumentUploadAccepted, -]) - -export const zDeleteKnowledgeSpacesByIdDocumentsBulkBody = z.object({ - documents: z - .array( - z.object({ - documentId: z.uuid(), - expectedRevision: z.int().gt(0), - }), - ) - .min(1), -}) - -export const zDeleteKnowledgeSpacesByIdDocumentsBulkHeaders = z.object({ - 'idempotency-key': z.string().min(8).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zDeleteKnowledgeSpacesByIdDocumentsBulkPath = z.object({ - id: z.uuid(), -}) - -/** - * Per-document durable deletions accepted - */ -export const zDeleteKnowledgeSpacesByIdDocumentsBulkResponse = zDurableBulkDeletionAccepted - -export const zPostKnowledgeSpacesByIdDocumentsBulkBody = z.object({ - files: z.array(z.custom()).min(1), - targets: z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdDocumentsBulkHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdDocumentsBulkPath = z.object({ - id: z.uuid(), -}) - -/** - * Accepted bulk document upload for durable compilation - */ -export const zPostKnowledgeSpacesByIdDocumentsBulkResponse = zBulkDocumentUploadAccepted - -export const zPostKnowledgeSpacesByIdDocumentsBulkReindexBody = z.object({ - all: z.boolean().optional(), - documentIds: z.array(z.uuid()).min(1).optional(), -}) - -export const zPostKnowledgeSpacesByIdDocumentsBulkReindexHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostKnowledgeSpacesByIdDocumentsBulkReindexPath = z.object({ - id: z.uuid(), -}) - -/** - * Accepted bulk document reindex - */ -export const zPostKnowledgeSpacesByIdDocumentsBulkReindexResponse = zBulkDocumentReindexResult - -export const zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdBody = z.object({ - expectedRevision: z.int().gt(0), -}) - -export const zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdHeaders = z.object({ - 'idempotency-key': z.string().min(8).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -/** - * Durable deletion accepted - */ -export const zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdResponse = zDurableDeletionAccepted - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -/** - * Document asset - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdResponse = zDocumentAsset - -export const zGetKnowledgeSpacesByIdLogicalDocumentsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdLogicalDocumentsPath = z.object({ - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdLogicalDocumentsQuery = z.object({ - cursor: z.string().min(1).max(2048).optional(), - limit: z.int().gte(1).lte(100).optional(), -}) - -/** - * Logical documents - */ -export const zGetKnowledgeSpacesByIdLogicalDocumentsResponse = zLogicalDocumentList - -export const zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdBody = z.object({ - expectedRevision: z.int().gt(0), -}) - -export const zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdHeaders = z.object({ - 'idempotency-key': z.string().min(8).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -/** - * Durable deletion accepted - */ -export const zDeleteKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponse = - zDurableDeletionAccepted - -export const zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -/** - * Logical document - */ -export const zGetKnowledgeSpacesByIdLogicalDocumentsByDocumentIdResponse = zLogicalDocument - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlinePath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -/** - * Document outline - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdOutlineResponse = zDocumentOutline - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsQuery = z.object({ - cursor: z.string().min(1).max(2048).optional(), - limit: z.int().gte(1).lte(100).optional(), -}) - -/** - * Immutable document revision history - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsResponse = zDocumentRevisionList - -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackBody = - z.object({ - expectedActiveRevision: z.int().gt(0), - expectedRowVersion: z.int().gte(0), - }) - -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackHeaders = - z.object({ - 'x-trace-id': z.string().optional(), - }) - -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackPath = - z.object({ - documentId: z.uuid(), - id: z.uuid(), - revision: z.int().gt(0), - }) - -/** - * Rollback candidate compilation accepted - */ -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionRollbackResponse = - zDocumentProcessingTask - -export const zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataBody = z.object({ - expectedRowVersion: z.int().gte(0), - patch: z.record(z.string(), z.unknown()), -}) - -export const zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -/** - * Updated user metadata - */ -export const zPatchKnowledgeSpacesByIdDocumentsByDocumentIdMetadataResponse = zLogicalDocument - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksHeaders = - z.object({ - 'x-trace-id': z.string().optional(), - }) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), - revision: z.int().gt(0), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksQuery = z.object({ - cursor: z.string().min(1).max(2048).optional(), - limit: z.int().gte(1).lte(100).optional(), - query: z.string().min(1).max(512).optional(), -}) - -/** - * Revision-scoped chunks - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksResponse = - zDocumentChunkList - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdHeaders = - z.object({ - 'x-trace-id': z.string().optional(), - }) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdPath = - z.object({ - documentId: z.uuid(), - id: z.uuid(), - revision: z.int().gt(0), - chunkId: z.uuid(), - }) - -/** - * Revision-scoped chunk - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdResponse = - zDocumentRevisionChunk - -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateBody = - z.object({ - enabled: z.boolean(), - }) - -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateHeaders = - z.object({ - 'x-trace-id': z.string().optional(), - }) - -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStatePath = - z.object({ - documentId: z.uuid(), - id: z.uuid(), - revision: z.int().gt(0), - chunkId: z.uuid(), - }) - -/** - * Candidate publication accepted - */ -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunksByChunkIdStateResponse = - zDocumentChunkStateChangeAccepted - -export const zGetKnowledgeSpacesByIdProcessingTasksHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdProcessingTasksPath = z.object({ - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdProcessingTasksQuery = z.object({ - cursor: z.string().min(1).max(2048).optional(), - limit: z.int().gte(1).lte(100).optional(), -}) - -/** - * Space processing tasks - */ -export const zGetKnowledgeSpacesByIdProcessingTasksResponse = zDocumentProcessingTaskList - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksQuery = z.object({ - cursor: z.string().min(1).max(2048).optional(), - limit: z.int().gte(1).lte(100).optional(), -}) - -/** - * Document processing tasks - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksResponse = - zDocumentProcessingTaskList - -export const zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdHeaders = - z.object({ - 'x-trace-id': z.string().optional(), - }) - -export const zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), - taskId: z.uuid(), -}) - -/** - * Canceled processing task - */ -export const zDeleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponse = - zDocumentProcessingTask - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), - taskId: z.uuid(), -}) - -/** - * Processing task polling snapshot - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdResponse = - zDocumentProcessingTask - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsHeaders = - z.object({ - 'last-event-id': z.string().optional(), - 'x-trace-id': z.string().optional(), - }) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsPath = - z.object({ - documentId: z.uuid(), - id: z.uuid(), - taskId: z.uuid(), - }) - -/** - * Progress SSE snapshot; reconnect using polling or Last-Event-ID - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEventsResponse = - zDocumentProcessingTaskEvent - -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryHeaders = - z.object({ - 'x-trace-id': z.string().optional(), - }) - -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryPath = - z.object({ - documentId: z.uuid(), - id: z.uuid(), - taskId: z.uuid(), - }) - -/** - * Retried processing task - */ -export const zPostKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetryResponse = - zDocumentProcessingTask - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -/** - * Active document index settings - */ -export const zGetKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponse = zDocumentSettingsHead - -export const zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsBody = z.object({ - expectedSettingsHeadRevision: z.int().gt(0).nullable(), - settings: z.object({ - chunkOverlap: z.int().gte(0).lte(8191), - chunkSize: z.int().gte(128).lte(8192), - enableGraph: z.boolean(), - enablePageIndex: z.boolean(), - language: z.string().min(2).max(64).optional(), - }), -}) - -export const zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsPath = z.object({ - documentId: z.uuid(), - id: z.uuid(), -}) - -/** - * Versioned settings reindex accepted - */ -export const zPutKnowledgeSpacesByIdDocumentsByDocumentIdSettingsResponse = zDocumentReindexAccepted - -export const zDeleteJobsByIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zDeleteJobsByIdPath = z.object({ - id: z.string().min(1), -}) - -/** - * Canceled document compilation job - */ -export const zDeleteJobsByIdResponse = zDocumentCompilationJob - -export const zGetJobsByIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetJobsByIdPath = z.object({ - id: z.string().min(1), -}) - -/** - * Document compilation job status - */ -export const zGetJobsByIdResponse = zDocumentCompilationJob - -export const zPostJobsByIdRetryHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zPostJobsByIdRetryPath = z.object({ - id: z.string().min(1), -}) - -/** - * Reactivated document compilation attempt - */ -export const zPostJobsByIdRetryResponse = zDocumentCompilationJob - -export const zGetDeletionJobsByJobIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetDeletionJobsByJobIdPath = z.object({ - jobId: z.uuid(), -}) - -/** - * Durable deletion status - */ -export const zGetDeletionJobsByJobIdResponse = zDurableDeletionJob - -export const zPostDeletionJobsByJobIdRetryHeaders = z.object({ - 'idempotency-key': z.string().min(8).max(255), - 'x-trace-id': z.string().optional(), -}) - -export const zPostDeletionJobsByJobIdRetryPath = z.object({ - jobId: z.uuid(), -}) - -/** - * Durable deletion accepted - */ -export const zPostDeletionJobsByJobIdRetryResponse = zDurableDeletionAccepted - -export const zGetBulkJobsByIdHeaders = z.object({ - 'x-trace-id': z.string().optional(), -}) - -export const zGetBulkJobsByIdPath = z.object({ - id: z.string().min(1), -}) - -/** - * Bulk operation progress - */ -export const zGetBulkJobsByIdResponse = zBulkOperationProgress diff --git a/packages/contracts/knowledge-fs-contract.test.mjs b/packages/contracts/knowledge-fs-contract.test.mjs deleted file mode 100644 index f88da6b1eb8..00000000000 --- a/packages/contracts/knowledge-fs-contract.test.mjs +++ /dev/null @@ -1,47 +0,0 @@ -import { createHash } from 'node:crypto' -import { readFile } from 'node:fs/promises' -import { dirname, join } from 'node:path' -import { fileURLToPath } from 'node:url' -import { describe, expect, it } from 'vitest' -import { - knowledgeFsGeneratedArtifactSha256, - knowledgeFsSourceOpenapiSha256, -} from './generated/knowledge-fs/metadata.gen' -import { getStreamingOperationIds } from './scripts/knowledge-fs-contract-utils.mjs' - -const packageRoot = dirname(fileURLToPath(import.meta.url)) - -describe('KnowledgeFS contract generation', () => { - it.each(['200', '2XX'])('detects an SSE response declared with %s', (status) => { - expect( - getStreamingOperationIds({ - paths: { - '/tasks/{id}/events': { - get: { - operationId: 'streamTaskEvents', - responses: { - [status]: { - content: { - 'text/event-stream': {}, - }, - }, - }, - }, - }, - }, - }), - ).toEqual(['streamTaskEvents']) - }) - - it('matches the pinned source contract and committed generated artifacts', async () => { - const lock = JSON.parse( - await readFile(join(packageRoot, '../../api/knowledge-fs-contract.lock.json'), 'utf8'), - ) - - expect(knowledgeFsSourceOpenapiSha256).toBe(lock.openapiSha256) - for (const [fileName, expectedSha256] of Object.entries(knowledgeFsGeneratedArtifactSha256)) { - const content = await readFile(join(packageRoot, 'generated/knowledge-fs', fileName)) - expect(createHash('sha256').update(content).digest('hex'), fileName).toBe(expectedSha256) - } - }) -}) diff --git a/packages/contracts/openapi-ts.knowledge-fs.config.ts b/packages/contracts/openapi-ts.knowledge-fs.config.ts deleted file mode 100644 index 7270068414d..00000000000 --- a/packages/contracts/openapi-ts.knowledge-fs.config.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { $, defineConfig } from '@hey-api/openapi-ts' - -const input = process.env.KNOWLEDGE_FS_OPENAPI -const outputPath = process.env.KNOWLEDGE_FS_OUTPUT ?? 'generated/knowledge-fs' - -if (!input) throw new Error('KNOWLEDGE_FS_OPENAPI must point to the filtered pinned export') - -export default defineConfig({ - input, - logs: { - file: false, - }, - output: { - clean: true, - entryFile: false, - fileName: { - suffix: '.gen', - }, - path: outputPath, - }, - parser: { - patch: { - input: (spec) => { - const paths = spec.paths as Record | undefined - if (!paths) return - - for (const [path, pathItem] of Object.entries(paths)) { - delete paths[path] - paths[`/knowledge-fs${path}`] = pathItem - } - }, - }, - }, - plugins: [ - { - comments: false, - name: '@hey-api/typescript', - }, - { - name: 'zod', - '~resolvers': { - string: (ctx) => { - if (ctx.schema.format === 'binary') - return $(ctx.symbols.z) - .attr('custom') - .call() - .generic($.type.or($.type('Blob'), $.type('File'))) - - return undefined - }, - }, - }, - { - contracts: { - strategy: 'single', - }, - name: 'orpc', - validator: 'zod', - }, - ], -}) diff --git a/packages/contracts/package.json b/packages/contracts/package.json index ac0c28e7911..1435cf20c28 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -19,16 +19,11 @@ "./enterprise/*": { "types": "./generated/enterprise/*.ts", "import": "./generated/enterprise/*.ts" - }, - "./knowledge-fs/*": { - "types": "./generated/knowledge-fs/*.ts", - "import": "./generated/knowledge-fs/*.ts" } }, "scripts": { "gen-api-contract": "uv run --project ../../api ../../api/dev/generate_swagger_specs.py --output-dir openapi && uv run --project ../../api ../../api/dev/generate_fastopenapi_specs.py --output-dir openapi && node -e \"fs.rmSync('generated/api', { recursive: true, force: true })\" && openapi-ts -f openapi-ts.api.config.ts && vp fmt generated/api", "gen-enterprise-contract": "openapi-ts -f openapi-ts.enterprise.config.ts", - "gen-knowledge-fs-contract": "node scripts/generate-knowledge-fs-contract.mjs", "test": "vp test", "type-check": "tsc" }, diff --git a/packages/contracts/scripts/generate-knowledge-fs-contract.mjs b/packages/contracts/scripts/generate-knowledge-fs-contract.mjs deleted file mode 100644 index 962f51c6c0a..00000000000 --- a/packages/contracts/scripts/generate-knowledge-fs-contract.mjs +++ /dev/null @@ -1,119 +0,0 @@ -import { execFileSync } from 'node:child_process' -import { createHash } from 'node:crypto' -import { mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { dirname, join, resolve } from 'node:path' -import { fileURLToPath } from 'node:url' -import { getStreamingOperationIds } from './knowledge-fs-contract-utils.mjs' - -const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') -const workspaceRoot = resolve(packageRoot, '../..') -const repository = resolve( - process.env.KNOWLEDGE_FS_REPO ?? resolve(workspaceRoot, '../knowledge-fs'), -) -const temporaryDirectory = await mkdtemp(join(tmpdir(), 'dify-knowledge-fs-types-')) - -try { - const openapiPath = join(temporaryDirectory, 'knowledge-fs.console.json') - run( - 'uv', - [ - 'run', - '--project', - resolve(workspaceRoot, 'api'), - resolve(workspaceRoot, 'api/dev/generate_knowledge_fs_contract.py'), - '--repository', - repository, - '--check', - '--output-openapi', - openapiPath, - ], - workspaceRoot, - ) - run('pnpm', ['exec', 'openapi-ts', '-f', 'openapi-ts.knowledge-fs.config.ts'], packageRoot, { - KNOWLEDGE_FS_OPENAPI: openapiPath, - }) - await patchStreamingContracts(openapiPath) - run('pnpm', ['exec', 'vp', 'fmt', 'generated/knowledge-fs'], packageRoot) - await writeContractMetadata(openapiPath, await generatedArtifactSha256()) - run('pnpm', ['exec', 'vp', 'fmt', 'generated/knowledge-fs/metadata.gen.ts'], packageRoot) -} finally { - await rm(temporaryDirectory, { force: true, recursive: true }) -} - -async function patchStreamingContracts(openapiPath) { - const document = JSON.parse(await readFile(openapiPath, 'utf8')) - const streamingOperationIds = getStreamingOperationIds(document) - if (streamingOperationIds.length === 0) return - - const outputPath = join(packageRoot, 'generated/knowledge-fs/orpc.gen.ts') - let source = await readFile(outputPath, 'utf8') - source = replaceOnce( - source, - "import { oc } from '@orpc/contract'", - "import { eventIterator, oc } from '@orpc/contract'", - ) - - for (const operationId of streamingOperationIds) { - const responseSchema = `z${capitalize(operationId)}Response` - source = replaceOnce( - source, - `.output(${responseSchema})`, - `.output(eventIterator(${responseSchema}))`, - ) - } - - await writeFile(outputPath, source) -} - -function capitalize(value) { - return value.charAt(0).toUpperCase() + value.slice(1) -} - -function replaceOnce(source, target, replacement) { - const firstIndex = source.indexOf(target) - if (firstIndex === -1 || source.indexOf(target, firstIndex + target.length) !== -1) - throw new Error(`Expected exactly one generated occurrence of ${target}`) - - return source.slice(0, firstIndex) + replacement + source.slice(firstIndex + target.length) -} - -async function generatedArtifactSha256() { - const generatedDirectory = join(packageRoot, 'generated/knowledge-fs') - const fileNames = (await readdir(generatedDirectory)) - .filter((fileName) => fileName.endsWith('.gen.ts') && fileName !== 'metadata.gen.ts') - .sort() - - return Object.fromEntries( - await Promise.all( - fileNames.map(async (fileName) => [ - fileName, - createHash('sha256') - .update(await readFile(join(generatedDirectory, fileName))) - .digest('hex'), - ]), - ), - ) -} - -async function writeContractMetadata(openapiPath, artifactSha256) { - const document = JSON.parse(await readFile(openapiPath, 'utf8')) - const source = [ - '// This file is auto-generated by scripts/generate-knowledge-fs-contract.mjs.', - '// Do not edit it manually.', - '', - `export const knowledgeFsSourceOpenapiSha256 = ${JSON.stringify(document['x-dify-source-openapi-sha256'])}`, - `export const knowledgeFsConsoleDeclarationsSha256 = ${JSON.stringify(document['x-dify-console-declarations-sha256'])}`, - `export const knowledgeFsGeneratedArtifactSha256 = ${JSON.stringify(artifactSha256, null, 2)} as const`, - '', - ].join('\n') - await writeFile(join(packageRoot, 'generated/knowledge-fs/metadata.gen.ts'), source) -} - -function run(command, args, cwd, extraEnv = {}) { - execFileSync(command, args, { - cwd, - env: { ...process.env, ...extraEnv }, - stdio: 'inherit', - }) -} diff --git a/packages/contracts/scripts/knowledge-fs-contract-utils.mjs b/packages/contracts/scripts/knowledge-fs-contract-utils.mjs deleted file mode 100644 index 3f607516f25..00000000000 --- a/packages/contracts/scripts/knowledge-fs-contract-utils.mjs +++ /dev/null @@ -1,19 +0,0 @@ -export function getStreamingOperationIds(document) { - return Object.values(document.paths ?? {}) - .flatMap((pathItem) => - Object.values(pathItem).flatMap((operation) => { - if (typeof operation !== 'object' || operation === null) return [] - const isEventStream = Object.entries(operation.responses ?? {}).some( - ([status, response]) => - (status === '2XX' || /^2\d\d$/.test(status)) && - typeof response === 'object' && - response !== null && - 'text/event-stream' in (response.content ?? {}), - ) - return isEventStream && typeof operation.operationId === 'string' - ? [operation.operationId] - : [] - }), - ) - .sort() -} diff --git a/web/context/system-features-state.ts b/web/context/system-features-state.ts index 2b6929dd862..dc48d32fc1c 100644 --- a/web/context/system-features-state.ts +++ b/web/context/system-features-state.ts @@ -17,3 +17,7 @@ export const deploymentEditionAtom = atom((get) => { export const brandingEnabledAtom = atom((get) => { return get(systemFeaturesAtom).branding.enabled }) + +export const knowledgeFsUploadEnabledAtom = atom((get) => { + return get(systemFeaturesAtom).knowledge_fs_upload_enabled +}) diff --git a/web/features/new-rag/__tests__/add-source-page.spec.tsx b/web/features/new-rag/__tests__/add-source-page.spec.tsx index 6e2b545752a..05dd135da9b 100644 --- a/web/features/new-rag/__tests__/add-source-page.spec.tsx +++ b/web/features/new-rag/__tests__/add-source-page.spec.tsx @@ -1,7 +1,5 @@ -import type { - GetKnowledgeSpacesByIdSourceConnectionsResponse, - GetSourceProvidersResponse, -} from '@dify/contracts/knowledge-fs/types.gen' +import type { DatasourceProviderAuthListResponse } from '@dify/contracts/api/console/auth/types.gen' +import type { SourceConnection, SourceProvider } from '../source-models' import { act, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { StrictMode } from 'react' @@ -9,14 +7,53 @@ import { render } from '@/test/console/render' import { AddSourcePage } from '../add-source-page' import { newKnowledgeSourceDraftStorageKey } from '../routes' +type GetKnowledgeSpacesByIdSourceConnectionsResponse = { + items: SourceConnection[] + nextCursor?: string +} +type GetSourceProvidersResponse = { items: SourceProvider[] } + const routerMock = vi.hoisted(() => ({ push: vi.fn(), replace: vi.fn(), })) +const connectFirecrawlButtonName = 'dataset.newKnowledge.connectProvider:{"provider":"Firecrawl"}' + vi.mock('@/next/navigation', () => ({ useRouter: () => routerMock })) const toastInfoMock = vi.hoisted(() => vi.fn()) +const providerApiResponse = vi.hoisted(() => (provider: SourceProvider) => ({ + auth_kinds: provider.authKinds, + available: provider.available, + capabilities: provider.capabilities, + configuration: provider.configuration.map((field) => ({ + description: field.description ?? null, + format: field.format ?? null, + name: field.name, + required: field.required, + secret: field.secret, + type: field.type, + })), + display_name: provider.displayName, + id: provider.id, + unavailable_reason: provider.unavailableReason ?? null, +})) +const connectionApiResponse = vi.hoisted(() => (connection: SourceConnection) => ({ + auth_kind: connection.authKind, + configuration: connection.configuration, + created_at: connection.createdAt, + error_code: connection.errorCode ?? null, + expires_at: connection.expiresAt ?? null, + id: connection.id, + knowledge_space_id: connection.knowledgeSpaceId, + name: connection.name, + provider_id: connection.providerId, + scopes: connection.scopes, + status: connection.status, + updated_at: connection.updatedAt, + version: connection.version, +})) vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { info: toastInfoMock }, @@ -25,12 +62,19 @@ vi.mock('@langgenius/dify-ui/toast', () => ({ type ConnectionsInfiniteData = { pages: GetKnowledgeSpacesByIdSourceConnectionsResponse[] } +const connectionInfiniteDataApiResponse = vi.hoisted(() => (data: ConnectionsInfiniteData) => ({ + pages: data.pages.map((page) => ({ + data: page.items.map(connectionApiResponse), + next_cursor: page.nextCursor ?? null, + })), +})) type ConnectionsInfiniteOptions = { enabled?: boolean - getNextPageParam: ( - lastPage: GetKnowledgeSpacesByIdSourceConnectionsResponse, - ) => string | undefined + getNextPageParam: (lastPage: { + data: ReturnType[] + next_cursor?: string | null + }) => string | null | undefined input: (pageParam: string | null) => unknown initialPageParam: string | null } @@ -52,6 +96,12 @@ const queryState = vi.hoisted(() => ({ isPending: false, refetch: vi.fn(), }, + datasourceAuth: { + data: { result: [] } as DatasourceProviderAuthListResponse | undefined, + error: null as unknown, + isPending: false, + refetch: vi.fn(), + }, })) const clientMock = vi.hoisted(() => ({ @@ -64,9 +114,10 @@ const queryClientMock = vi.hoisted(() => ({ })) const providerQueryOptionsMock = vi.hoisted(() => - vi.fn((options: { enabled?: boolean }) => ({ + vi.fn((options: { enabled?: boolean; select?: (data: unknown) => unknown }) => ({ enabled: options.enabled, queryKey: ['source-providers'], + select: options.select, })), ) const connectionInfiniteOptionsMock = vi.hoisted(() => @@ -76,6 +127,12 @@ const connectionInfiniteOptionsMock = vi.hoisted(() => })), ) const providerHookOptionsMock = vi.hoisted(() => vi.fn()) +const datasourceAuthQueryOptionsMock = vi.hoisted(() => + vi.fn((options: { enabled?: boolean }) => ({ + enabled: options.enabled, + queryKey: ['datasource-auth'], + })), +) const connectionHookOptionsMock = vi.hoisted(() => vi.fn()) vi.mock('@tanstack/react-query', async (importOriginal) => { @@ -84,11 +141,33 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { ...original, useInfiniteQuery: (options: unknown) => { connectionHookOptionsMock(options) - return queryState.connections + return { + ...queryState.connections, + data: queryState.connections.data + ? connectionInfiniteDataApiResponse(queryState.connections.data) + : undefined, + refetch: async () => { + const result = (await queryState.connections.refetch()) as + | { data?: ConnectionsInfiniteData; error?: unknown } + | undefined + if (!result?.data) return result + return { + ...result, + data: connectionInfiniteDataApiResponse(result.data), + } + }, + } }, - useQuery: (options: unknown) => { + useQuery: (options: { queryKey?: string[]; select?: (data: unknown) => unknown }) => { providerHookOptionsMock(options) - return queryState.providers + if (options.queryKey?.[0] === 'datasource-auth') return queryState.datasourceAuth + const raw = queryState.providers.data + ? { data: queryState.providers.data.items.map(providerApiResponse) } + : undefined + return { + ...queryState.providers, + data: raw && options.select ? options.select(raw) : raw, + } }, useQueryClient: () => queryClientMock, } @@ -97,18 +176,49 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { vi.mock('@/service/client', () => ({ consoleClient: { knowledgeFs: { - postKnowledgeSpacesByIdSourceConnections: clientMock.createConnection, - postKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefresh: clientMock.refreshConnection, + spaces: { + byControlSpaceId: { + sourceConnections: { + byConnectionId: { + refresh: { + post: async (input: unknown) => + connectionApiResponse(await clientMock.refreshConnection(input)), + }, + }, + post: async (input: unknown) => + connectionApiResponse(await clientMock.createConnection(input)), + }, + }, + }, }, }, consoleQuery: { - knowledgeFs: { - getSourceProviders: { - queryOptions: providerQueryOptionsMock, + auth: { + plugin: { + datasource: { + defaultList: { + get: { + queryOptions: datasourceAuthQueryOptionsMock, + }, + }, + }, }, - getKnowledgeSpacesByIdSourceConnections: { - infiniteOptions: connectionInfiniteOptionsMock, - key: vi.fn(() => ['source-connections']), + }, + knowledgeFs: { + spaces: { + byControlSpaceId: { + sourceConnections: { + get: { + infiniteOptions: connectionInfiniteOptionsMock, + key: vi.fn(() => ['source-connections']), + }, + }, + sourceProviders: { + get: { + queryOptions: providerQueryOptionsMock, + }, + }, + }, }, }, }, @@ -161,6 +271,69 @@ const firecrawlProvider: GetSourceProvidersResponse['items'][number] = { id: 'plugin-daemon-website', } +const difyManagedFirecrawlProvider: GetSourceProvidersResponse['items'][number] = { + authKinds: ['endpoint'], + available: true, + capabilities: ['website-crawl'], + configuration: [ + { + name: 'credentialId', + required: true, + secret: false, + type: 'string', + }, + { + name: 'pluginId', + required: true, + secret: false, + type: 'string', + }, + { + name: 'provider', + required: true, + secret: false, + type: 'string', + }, + { + name: 'datasource', + required: true, + secret: false, + type: 'string', + }, + { + name: 'providerKind', + required: true, + secret: false, + type: 'string', + }, + ], + displayName: 'Dify website crawl', + id: 'plugin-daemon-website', +} + +const firecrawlDatasourceAuth: DatasourceProviderAuthListResponse['result'][number] = { + author: 'langgenius', + credential_schema: [], + credentials_list: [ + { + avatar_url: null, + credential: {}, + id: 'firecrawl-credential-1', + is_default: true, + name: 'Default Firecrawl', + type: 'api-key', + }, + ], + description: { en_US: 'Firecrawl' }, + icon: 'icon.svg', + label: { en_US: 'Firecrawl' }, + name: 'firecrawl', + oauth_schema: null, + plugin_id: 'langgenius/firecrawl_datasource', + plugin_unique_identifier: 'langgenius/firecrawl_datasource:1.0.0@local', + provider: 'firecrawl', +} + const connection = ( status: 'provisioning' | 'active' | 'expired' | 'error' | 'revoked', version = 2, @@ -190,9 +363,13 @@ describe('AddSourcePage', () => { clientMock.refreshConnection.mockReset() queryState.connections.refetch.mockReset() queryState.providers.refetch.mockReset() + queryState.datasourceAuth.refetch.mockReset() queryState.providers.data = { items: [firecrawlProvider] } queryState.providers.error = null queryState.providers.isPending = false + queryState.datasourceAuth.data = { result: [] } + queryState.datasourceAuth.error = null + queryState.datasourceAuth.isPending = false queryState.connections.data = { pages: [{ items: [] }] } queryState.connections.error = null queryState.connections.hasNextPage = false @@ -213,18 +390,22 @@ describe('AddSourcePage', () => { expect(providerQueryOptionsMock).toHaveBeenCalledWith({ context: { silent: true }, enabled: true, - input: {}, + input: { params: { control_space_id: 'space-1' } }, retry: false, + select: expect.any(Function), }) const options = connectionInfiniteOptionsMock.mock.lastCall?.[0] expect(options).toBeDefined() if (!options) throw new Error('Expected connection infinite query options') - expect(options.input(null)).toEqual({ params: { id: 'space-1' }, query: { limit: 200 } }) + expect(options.input(null)).toEqual({ + params: { control_space_id: 'space-1' }, + query: { limit: 200 }, + }) expect(options.input('next')).toEqual({ - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, query: { cursor: 'next', limit: 200 }, }) - expect(options.getNextPageParam({ items: [], nextCursor: 'next' })).toBe('next') + expect(options.getNextPageParam({ data: [], next_cursor: 'next' })).toBe('next') expect(options.initialPageParam).toBeNull() expect(screen.getByRole('status')).toBeInTheDocument() }) @@ -534,7 +715,7 @@ describe('AddSourcePage', () => { ) await user.type(screen.getByLabelText(/Api Key/), 'secret-value') await user.type(screen.getByLabelText('Endpoint'), 'https://crawl.example.com') - await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.connectProvider' })) + await user.click(screen.getByRole('button', { name: connectFirecrawlButtonName })) await waitFor(() => expect(clientMock.createConnection).toHaveBeenCalledWith({ @@ -550,7 +731,7 @@ describe('AddSourcePage', () => { name: 'Firecrawl', providerId: 'plugin-daemon-website', }, - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, }), ) await screen.findByRole('status', { name: 'appApi.loading' }) @@ -562,6 +743,64 @@ describe('AddSourcePage', () => { expect(screen.queryByDisplayValue('secret-value')).not.toBeInTheDocument() }) + it('binds the default Dify Firecrawl credential for the real KnowledgeFS provider', async () => { + const user = userEvent.setup() + queryState.providers.data = { items: [difyManagedFirecrawlProvider] } + queryState.datasourceAuth.data = { result: [firecrawlDatasourceAuth] } + clientMock.createConnection.mockResolvedValue({ + ...connection('active'), + authKind: 'endpoint', + configuration: { + credentialId: 'firecrawl-credential-1', + datasource: 'crawl', + pluginId: 'langgenius/firecrawl_datasource', + provider: 'firecrawl', + providerKind: 'website', + }, + }) + + render() + await user.click(screen.getByRole('button', { name: connectFirecrawlButtonName })) + + await waitFor(() => + expect(clientMock.createConnection).toHaveBeenCalledWith({ + body: { + authKind: 'endpoint', + configuration: { + credentialId: 'firecrawl-credential-1', + datasource: 'crawl', + pluginId: 'langgenius/firecrawl_datasource', + provider: 'firecrawl', + providerKind: 'website', + }, + credentials: {}, + name: 'Firecrawl', + providerId: 'plugin-daemon-website', + }, + params: { control_space_id: 'space-1' }, + }), + ) + expect(screen.queryByLabelText(/Api Key/)).not.toBeInTheDocument() + }) + + it('opens Data Source settings when Dify has no Firecrawl credential', async () => { + const user = userEvent.setup() + queryState.providers.data = { items: [difyManagedFirecrawlProvider] } + + render() + expect(screen.queryByLabelText(/Api Key/)).not.toBeInTheDocument() + expect( + screen.queryByRole('button', { name: connectFirecrawlButtonName }), + ).not.toBeInTheDocument() + + await user.click( + screen.getByRole('button', { name: 'dataset.newKnowledge.openDataSourceSettings' }), + ) + + expect(routerMock.replace).toHaveBeenCalledWith('/integrations/data-source') + expect(clientMock.createConnection).not.toHaveBeenCalled() + }) + it('releases the parent history guard before the crawl preview owns navigation', async () => { const user = userEvent.setup() const historyBack = vi.spyOn(window.history, 'back').mockImplementation(() => undefined) @@ -572,7 +811,7 @@ describe('AddSourcePage', () => { screen.getByRole('button', { name: /^dataset\.newKnowledge\.configureProvider/ }), ) await user.type(screen.getByLabelText(/Api Key/), 'secret-value') - await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.connectProvider' })) + await user.click(screen.getByRole('button', { name: connectFirecrawlButtonName })) await waitFor(() => expect(historyBack).toHaveBeenCalledOnce()) expect(screen.queryByText(/dataset\.newKnowledge\.providerConnected/)).not.toBeInTheDocument() @@ -677,7 +916,7 @@ describe('AddSourcePage', () => { ) await user.type(screen.getByLabelText(/Api Key/), 'do-not-retain') await user.type(screen.getByLabelText('Endpoint'), 'https://crawl.example.com') - await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.connectProvider' })) + await user.click(screen.getByRole('button', { name: connectFirecrawlButtonName })) expect(await screen.findByText('dataset.newKnowledge.connectionFailed')).toBeInTheDocument() expect(screen.getByLabelText(/Api Key/)).toHaveValue('') @@ -696,10 +935,9 @@ describe('AddSourcePage', () => { screen.getByRole('button', { name: /^dataset\.newKnowledge\.configureProvider/ }), ) await user.type(screen.getByLabelText(/Api Key/), 'secret-value') - await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.connectProvider' })) + await user.click(screen.getByRole('button', { name: connectFirecrawlButtonName })) await waitFor(() => expect(clientMock.createConnection).toHaveBeenCalledOnce()) - await screen.findByRole('status', { name: 'appApi.loading' }) act(() => window.dispatchEvent(new PopStateEvent('popstate'))) expect(await screen.findByText(/dataset\.newKnowledge\.providerConnected/)).toBeInTheDocument() expect(screen.queryByText('dataset.newKnowledge.connectionFailed')).not.toBeInTheDocument() @@ -734,7 +972,7 @@ describe('AddSourcePage', () => { await user.type(screen.getByLabelText(/Api Key/), 'must-not-be-sent') await user.click(screen.getByRole('radio', { name: 'dataset.newKnowledge.authKind.endpoint' })) await user.type(screen.getByLabelText('Endpoint'), 'https://crawl.example.com') - await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.connectProvider' })) + await user.click(screen.getByRole('button', { name: connectFirecrawlButtonName })) await waitFor(() => expect(clientMock.createConnection).toHaveBeenCalledWith({ @@ -750,7 +988,7 @@ describe('AddSourcePage', () => { name: 'Firecrawl', providerId: 'plugin-daemon-website', }, - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, }), ) }) @@ -792,7 +1030,7 @@ describe('AddSourcePage', () => { await waitFor(() => expect(clientMock.refreshConnection).toHaveBeenCalledWith({ body: { expectedVersion: 2 }, - params: { connectionId: 'connection-1', id: 'space-1' }, + params: { connection_id: 'connection-1', control_space_id: 'space-1' }, }), ) expect(queryClientMock.invalidateQueries).toHaveBeenCalled() @@ -830,7 +1068,7 @@ describe('AddSourcePage', () => { await waitFor(() => expect(clientMock.refreshConnection).toHaveBeenLastCalledWith({ body: { expectedVersion: 3 }, - params: { connectionId: 'connection-1', id: 'space-1' }, + params: { connection_id: 'connection-1', control_space_id: 'space-1' }, }), ) }) @@ -853,7 +1091,7 @@ describe('AddSourcePage', () => { await waitFor(() => expect(clientMock.refreshConnection).toHaveBeenLastCalledWith({ body: { expectedVersion: 3 }, - params: { connectionId: 'connection-1', id: 'space-1' }, + params: { connection_id: 'connection-1', control_space_id: 'space-1' }, }), ) }) diff --git a/web/features/new-rag/__tests__/crawl-selection-form.spec.tsx b/web/features/new-rag/__tests__/crawl-selection-form.spec.tsx index a878aa38f7a..61f99736175 100644 --- a/web/features/new-rag/__tests__/crawl-selection-form.spec.tsx +++ b/web/features/new-rag/__tests__/crawl-selection-form.spec.tsx @@ -1,9 +1,9 @@ import type { - GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse, - GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponse, + CrawlPreviewPageList, Source, + SourceSyncPolicy, SourceWorkflowRun, -} from '@dify/contracts/knowledge-fs/types.gen' +} from '../source-models' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { act, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' @@ -13,19 +13,55 @@ import datasetTranslations from '@/i18n/en-US/dataset.json' import { render } from '@/test/console/render' import { CrawlSelectionForm } from '../crawl-selection-form' +type GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse = SourceSyncPolicy +type GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponse = CrawlPreviewPageList + const clientMock = vi.hoisted(() => ({ getPolicy: vi.fn(), getWorkflow: vi.fn(), selectPages: vi.fn(), updatePolicy: vi.fn(), })) +const policyApiResponse = vi.hoisted(() => (policy: SourceSyncPolicy) => ({ + created_at: policy.createdAt, + custom_interval_seconds: policy.customIntervalSeconds ?? null, + enabled: policy.enabled, + expected_source_version: policy.expectedSourceVersion, + id: policy.id, + knowledge_space_id: policy.knowledgeSpaceId, + mode: policy.mode, + next_run_at: policy.nextRunAt ?? null, + revision: policy.revision, + source_id: policy.sourceId, + updated_at: policy.updatedAt, +})) +const workflowApiResponse = vi.hoisted(() => (workflow: SourceWorkflowRun) => ({ + canceled_at: workflow.canceledAt ?? null, + checkpoint: workflow.checkpoint, + completed_at: workflow.completedAt ?? null, + created_at: workflow.createdAt, + cursor: workflow.cursor ?? null, + execution_attempts: workflow.executionAttempts, + id: workflow.id, + knowledge_space_id: workflow.knowledgeSpaceId, + kind: workflow.kind, + last_error_code: workflow.lastErrorCode ?? null, + max_execution_attempts: workflow.maxExecutionAttempts, + progress_completed: workflow.progressCompleted, + progress_failed: workflow.progressFailed, + progress_skipped: workflow.progressSkipped, + progress_total: workflow.progressTotal ?? null, + source_id: workflow.sourceId ?? null, + state: workflow.state, + updated_at: workflow.updatedAt, +})) const routerMock = vi.hoisted(() => ({ push: vi.fn() })) const queryClientMock = vi.hoisted(() => ({ invalidateQueries: vi.fn() })) const policyQueryOptionsMock = vi.hoisted(() => - vi.fn(({ input }) => ({ - queryFn: () => clientMock.getPolicy(input), - queryKey: ['sync-policy', input.params.sourceId], + vi.fn(({ input, select }) => ({ + queryFn: async () => select(policyApiResponse(await clientMock.getPolicy(input))), + queryKey: ['sync-policy', input.params.source_id], retry: false, })), ) @@ -40,27 +76,51 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { vi.mock('@/service/client', () => ({ consoleClient: { knowledgeFs: { - getKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy: clientMock.getPolicy, - getKnowledgeSpacesByIdSourceWorkflowsByRunId: clientMock.getWorkflow, + spaces: { + byControlSpaceId: { + sourceWorkflows: { + byRunId: { + get: async (input: unknown) => + workflowApiResponse(await clientMock.getWorkflow(input)), + selection: { + post: async (input: unknown) => + workflowApiResponse(await clientMock.selectPages(input)), + }, + }, + }, + sources: { + bySourceId: { + syncPolicy: { + get: async (input: unknown) => policyApiResponse(await clientMock.getPolicy(input)), + put: async (input: unknown) => + policyApiResponse(await clientMock.updatePolicy(input)), + }, + }, + get: { + key: vi.fn(() => ['knowledge-sources']), + }, + }, + }, + }, }, }, consoleQuery: { knowledgeFs: { - getKnowledgeSpacesByIdSources: { - key: vi.fn(() => ['knowledge-sources']), - }, - getKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy: { - queryOptions: policyQueryOptionsMock, - }, - postKnowledgeSpacesByIdSourceWorkflowsByRunIdSelection: { - mutationOptions: vi.fn(() => ({ - mutationFn: (input: unknown) => clientMock.selectPages(input), - })), - }, - putKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy: { - mutationOptions: vi.fn(() => ({ - mutationFn: (input: unknown) => clientMock.updatePolicy(input), - })), + spaces: { + byControlSpaceId: { + sources: { + bySourceId: { + syncPolicy: { + get: { + queryOptions: policyQueryOptionsMock, + }, + }, + }, + get: { + key: vi.fn(() => ['knowledge-sources']), + }, + }, + }, }, }, }, @@ -346,13 +406,13 @@ describe('CrawlSelectionForm', () => { expectedSourceVersion: 3, mode: 'custom', }, - params: { id: 'space-1', sourceId: 'source-1' }, + params: { control_space_id: 'space-1', source_id: 'source-1' }, }) expect(clientMock.selectPages).toHaveBeenCalledOnce() expect(clientMock.selectPages).toHaveBeenCalledWith({ body: { pageIds: ['page-1'] }, headers: { 'Idempotency-Key': expect.any(String) }, - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, }) selectionRequest.resolve({ ...run, checkpoint: 'import', state: 'queued' }) @@ -499,7 +559,7 @@ describe('CrawlSelectionForm', () => { expectedSourceVersion: 3, mode, }, - params: { id: 'space-1', sourceId: 'source-1' }, + params: { control_space_id: 'space-1', source_id: 'source-1' }, }) expect(clientMock.selectPages).toHaveBeenCalledOnce() }, @@ -587,7 +647,7 @@ describe('CrawlSelectionForm', () => { expectedSourceVersion: 4, mode: 'manual', }, - params: { id: 'space-1', sourceId: 'source-1' }, + params: { control_space_id: 'space-1', source_id: 'source-1' }, }) expect(clientMock.selectPages).toHaveBeenCalledOnce() }) @@ -628,7 +688,7 @@ describe('CrawlSelectionForm', () => { expectedSourceVersion: 3, mode: 'provider', }, - params: { id: 'space-1', sourceId: 'source-1' }, + params: { control_space_id: 'space-1', source_id: 'source-1' }, }) expect(clientMock.selectPages).toHaveBeenCalledOnce() }) diff --git a/web/features/new-rag/__tests__/create-knowledge-page.spec.tsx b/web/features/new-rag/__tests__/create-knowledge-page.spec.tsx index f02c41c5d8b..8cb89da9855 100644 --- a/web/features/new-rag/__tests__/create-knowledge-page.spec.tsx +++ b/web/features/new-rag/__tests__/create-knowledge-page.spec.tsx @@ -7,8 +7,7 @@ import { newKnowledgeSourceDraftStorageKey } from '../routes' const serviceMock = vi.hoisted(() => ({ create: vi.fn(), - getPolicy: vi.fn(), - patchPolicy: vi.fn(), + getDefaultModel: vi.fn(), upload: vi.fn(), uploadBulk: vi.fn(), listKey: vi.fn(() => ['console', 'knowledgeFs', 'listKnowledgeSpaces']), @@ -28,6 +27,11 @@ const permissionStateMock = vi.hoisted(() => ({ keys: ['dataset.create_and_management', 'dataset.acl.access_config'], })) +const systemFeaturesStateMock = vi.hoisted(() => ({ + atom: Symbol('knowledgeFsUploadEnabledAtom'), + uploadEnabled: true, +})) + vi.mock('@/next/navigation', () => ({ useRouter: () => routerMock, useSearchParams: () => ({ @@ -39,6 +43,10 @@ vi.mock('@/context/permission-state', () => ({ workspacePermissionKeysAtom: permissionStateMock.atom, })) +vi.mock('@/context/system-features-state', () => ({ + knowledgeFsUploadEnabledAtom: systemFeaturesStateMock.atom, +})) + vi.mock('jotai', async (importOriginal) => { const original = await importOriginal() return { @@ -46,40 +54,62 @@ vi.mock('jotai', async (importOriginal) => { useAtomValue: (atom: unknown) => atom === permissionStateMock.atom ? permissionStateMock.keys - : original.useAtomValue(atom as Parameters[0]), + : atom === systemFeaturesStateMock.atom + ? systemFeaturesStateMock.uploadEnabled + : original.useAtomValue(atom as Parameters[0]), } }) vi.mock('@/service/client', () => ({ consoleClient: { knowledgeFs: { - createKnowledgeSpace: serviceMock.create, - getKnowledgeSpacesByIdAccessPolicy: serviceMock.getPolicy, - patchKnowledgeSpacesByIdAccessPolicy: serviceMock.patchPolicy, - postKnowledgeSpacesByIdDocuments: serviceMock.upload, - postKnowledgeSpacesByIdDocumentsBulk: serviceMock.uploadBulk, + spaces: { + post: serviceMock.create, + }, + }, + workspaces: { + current: { + defaultModel: { + get: serviceMock.getDefaultModel, + }, + }, }, }, consoleQuery: { knowledgeFs: { - listKnowledgeSpaces: { - key: serviceMock.listKey, + spaces: { + get: { + key: serviceMock.listKey, + }, }, }, }, })) const createdKnowledge = { - configurationStatus: 'ready', - createdAt: '2026-07-20T00:00:00Z', - id: 'e735c1dc-d2b8-4dc4-86dc-abaf2fb7d084', - name: 'Product handbook', - revision: 1, - slug: 'product-handbook', - tenantId: 'tenant-1', - updatedAt: '2026-07-20T00:00:00Z', + control_space_id: 'e735c1dc-d2b8-4dc4-86dc-abaf2fb7d084', + operation_id: 'operation-1', + state: 'provisioning' as const, } +vi.mock('../knowledge-fs-upload', () => ({ + uploadKnowledgeFsDocuments: async ( + knowledgeSpaceId: string, + uploads: Array<{ file: File; id: string }>, + ) => { + const files = uploads.map(({ file }) => file) + if (files.length === 1) + return serviceMock.upload({ + body: { file: files[0] }, + params: { control_space_id: knowledgeSpaceId }, + }) + return serviceMock.uploadBulk({ + body: { files }, + params: { control_space_id: knowledgeSpaceId }, + }) + }, +})) + function renderPage( queryClient = new QueryClient({ defaultOptions: { mutations: { retry: false } } }), ) { @@ -110,20 +140,20 @@ describe('CreateKnowledgePage', () => { vi.clearAllMocks() globalThis.sessionStorage.clear() serviceMock.create.mockResolvedValue(createdKnowledge) - serviceMock.getPolicy.mockResolvedValue({ - id: 'policy-1', - ownerSubjectId: 'user-1', - partialMemberSubjectIds: [], - revision: 4, - visibility: 'only_me', - }) - serviceMock.patchPolicy.mockResolvedValue({ - id: 'policy-1', - ownerSubjectId: 'user-1', - partialMemberSubjectIds: [], - revision: 5, - visibility: 'all_members', - }) + serviceMock.getDefaultModel.mockImplementation(({ query }: { query: { model_type: string } }) => + Promise.resolve({ + data: { + model: query.model_type === 'llm' ? 'echo' : 'embed', + model_type: query.model_type, + provider: { + provider: + query.model_type === 'llm' + ? 'kurokobo/fake_models/fake_models' + : 'langgenius/cohere/cohere', + }, + }, + }), + ) serviceMock.upload.mockResolvedValue({ id: 'document-1', }) @@ -133,6 +163,7 @@ describe('CreateKnowledgePage', () => { items: [], }) permissionStateMock.keys = ['dataset.create_and_management', 'dataset.acl.access_config'] + systemFeaturesStateMock.uploadEnabled = true navigationMock.startMode = null vi.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue( 'a9c36c57-2d84-44d6-a36d-841f0d92a179', @@ -174,12 +205,29 @@ describe('CreateKnowledgePage', () => { expect(serviceMock.create).toHaveBeenCalledWith({ body: { description: 'Internal answers', - idempotencyKey: 'a9c36c57-2d84-44d6-a36d-841f0d92a179', + embedding: { + model: 'embed', + plugin_id: 'langgenius/cohere', + provider: 'cohere', + }, + idempotency_key: 'a9c36c57-2d84-44d6-a36d-841f0d92a179', name: 'Product handbook', + retrieval: { + default_mode: 'fast', + reasoning_model: { + model: 'echo', + plugin_id: 'kurokobo/fake_models', + provider: 'fake_models', + }, + rerank: { enabled: false }, + score_threshold: { enabled: false, stage: 'mode-final' }, + top_k: 10, + }, + slug: 'product-handbook-a9c36c572d84', + visibility: 'only_me', }, }) }) - expect(serviceMock.getPolicy).not.toHaveBeenCalled() expect(invalidate).toHaveBeenCalledWith({ queryKey: ['console', 'knowledgeFs', 'listKnowledgeSpaces'], }) @@ -188,7 +236,7 @@ describe('CreateKnowledgePage', () => { ) }) - it('defaults authorized users to the Figma all-members policy and updates its revision', async () => { + it('creates the default all-members visibility atomically', async () => { const user = userEvent.setup() renderPage() await fillRequiredFields(user) @@ -199,13 +247,8 @@ describe('CreateKnowledgePage', () => { await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) await waitFor(() => { - expect(serviceMock.patchPolicy).toHaveBeenCalledWith({ - body: { - expectedRevision: 4, - partialMemberSubjectIds: [], - visibility: 'all_members', - }, - params: { id: createdKnowledge.id }, + expect(serviceMock.create).toHaveBeenCalledWith({ + body: expect.objectContaining({ visibility: 'all_team_members' }), }) }) }) @@ -226,7 +269,9 @@ describe('CreateKnowledgePage', () => { await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) await waitFor(() => expect(serviceMock.create).toHaveBeenCalledOnce()) - expect(serviceMock.getPolicy).not.toHaveBeenCalled() + expect(serviceMock.create).toHaveBeenCalledWith({ + body: expect.objectContaining({ visibility: 'only_me' }), + }) }) it('prevents duplicate pending submissions', async () => { @@ -263,11 +308,64 @@ describe('CreateKnowledgePage', () => { await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) await waitFor(() => expect(serviceMock.create).toHaveBeenCalledTimes(2)) - expect(serviceMock.create.mock.calls[0]?.[0].body.idempotencyKey).toBe( - serviceMock.create.mock.calls[1]?.[0].body.idempotencyKey, + expect(serviceMock.create.mock.calls[0]?.[0].body.idempotency_key).toBe( + serviceMock.create.mock.calls[1]?.[0].body.idempotency_key, ) }) + it('unlocks editable fields and rotates the idempotency key after model preflight fails', async () => { + const user = userEvent.setup() + vi.mocked(globalThis.crypto.randomUUID) + .mockReturnValueOnce('11111111-1111-4111-8111-111111111111') + .mockReturnValueOnce('22222222-2222-4222-8222-222222222222') + serviceMock.getDefaultModel.mockImplementation(({ query }: { query: { model_type: string } }) => + Promise.resolve( + query.model_type === 'llm' + ? { + data: { + model: 'echo', + provider: { provider: 'kurokobo/fake_models/fake_models' }, + }, + } + : { data: null }, + ), + ) + renderPage() + await fillRequiredFields(user) + + await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) + + expect(await screen.findByRole('alert')).toHaveTextContent('dataset.newKnowledge.createFailed') + const nameInput = screen.getByRole('textbox', { name: 'dataset.newKnowledge.name' }) + expect(nameInput).toBeEnabled() + expect(serviceMock.create).not.toHaveBeenCalled() + + serviceMock.getDefaultModel.mockImplementation(({ query }: { query: { model_type: string } }) => + Promise.resolve({ + data: { + model: query.model_type === 'llm' ? 'echo' : 'embed', + provider: { + provider: + query.model_type === 'llm' + ? 'kurokobo/fake_models/fake_models' + : 'langgenius/cohere/cohere', + }, + }, + }), + ) + await user.clear(nameInput) + await user.type(nameInput, 'Updated handbook') + await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) + + await waitFor(() => expect(serviceMock.create).toHaveBeenCalledOnce()) + expect(serviceMock.create).toHaveBeenCalledWith({ + body: expect.objectContaining({ + idempotency_key: '22222222-2222-4222-8222-222222222222', + name: 'Updated handbook', + }), + }) + }) + it.each([400, 401, 403, 422])( 'unlocks editable fields and rotates the idempotency key after a definitive %s rejection', async (status) => { @@ -290,11 +388,11 @@ describe('CreateKnowledgePage', () => { await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) await waitFor(() => expect(serviceMock.create).toHaveBeenCalledTimes(2)) - expect(serviceMock.create.mock.calls[0]?.[0].body.idempotencyKey).toBe( + expect(serviceMock.create.mock.calls[0]?.[0].body.idempotency_key).toBe( '11111111-1111-4111-8111-111111111111', ) expect(serviceMock.create.mock.calls[1]?.[0].body).toMatchObject({ - idempotencyKey: '22222222-2222-4222-8222-222222222222', + idempotency_key: '22222222-2222-4222-8222-222222222222', name: 'Updated handbook', }) }, @@ -319,23 +417,30 @@ describe('CreateKnowledgePage', () => { await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) await waitFor(() => expect(serviceMock.create).toHaveBeenCalledTimes(2)) - expect(serviceMock.create.mock.calls[0]?.[0].body.idempotencyKey).toBe( - serviceMock.create.mock.calls[1]?.[0].body.idempotencyKey, + expect(serviceMock.create.mock.calls[0]?.[0].body.idempotency_key).toBe( + serviceMock.create.mock.calls[1]?.[0].body.idempotency_key, ) }, ) - it('safely resumes the permission step after a partial failure', async () => { + it('safely resumes a downstream upload after the control space is created', async () => { const user = userEvent.setup() + navigationMock.startMode = 'upload' const queryClient = new QueryClient({ defaultOptions: { mutations: { retry: false } } }) const invalidate = vi.spyOn(queryClient, 'invalidateQueries') - serviceMock.patchPolicy.mockRejectedValueOnce(new Error('policy update unavailable')) + serviceMock.upload.mockRejectedValueOnce(new Error('upload unavailable')) renderPage(queryClient) + await user.upload( + screen.getByLabelText('dataset.newKnowledge.uploadFiles', { + selector: 'input[type="file"]', + }), + new File(['content'], 'handbook.md', { type: 'text/markdown' }), + ) await fillRequiredFields(user) await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) expect(await screen.findByRole('alert')).toHaveTextContent( - 'dataset.newKnowledge.permissionUpdateFailed', + 'dataset.newKnowledge.documentUploadFailed', ) expect(invalidate).toHaveBeenCalledWith({ queryKey: ['console', 'knowledgeFs', 'listKnowledgeSpaces'], @@ -346,44 +451,28 @@ describe('CreateKnowledgePage', () => { await user.type(nameInput, ' changed') await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) - await waitFor(() => expect(serviceMock.patchPolicy).toHaveBeenCalledTimes(2)) + await waitFor(() => expect(serviceMock.upload).toHaveBeenCalledTimes(2)) expect(serviceMock.create).toHaveBeenCalledOnce() expect(routerMock.replace).toHaveBeenCalledWith( - '/datasets/new/e735c1dc-d2b8-4dc4-86dc-abaf2fb7d084/sources', + '/datasets/new/e735c1dc-d2b8-4dc4-86dc-abaf2fb7d084/documents', ) }) - it('converges after a permission update succeeds but its response is lost', async () => { + it('converges after an atomic creation response is lost', async () => { const user = userEvent.setup() - serviceMock.getPolicy - .mockResolvedValueOnce({ - id: 'policy-1', - ownerSubjectId: 'user-1', - partialMemberSubjectIds: [], - revision: 4, - visibility: 'only_me', - }) - .mockResolvedValueOnce({ - id: 'policy-1', - ownerSubjectId: 'user-1', - partialMemberSubjectIds: [], - revision: 5, - visibility: 'all_members', - }) - serviceMock.patchPolicy.mockRejectedValueOnce(new Error('response lost')) + serviceMock.create.mockRejectedValueOnce(new Error('response lost')) renderPage() await fillRequiredFields(user) await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) - expect(await screen.findByRole('alert')).toHaveTextContent( - 'dataset.newKnowledge.permissionUpdateFailed', - ) + expect(await screen.findByRole('alert')).toHaveTextContent('dataset.newKnowledge.createFailed') await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) await waitFor(() => expect(routerMock.replace).toHaveBeenCalledOnce()) - expect(serviceMock.create).toHaveBeenCalledOnce() - expect(serviceMock.getPolicy).toHaveBeenCalledTimes(2) - expect(serviceMock.patchPolicy).toHaveBeenCalledOnce() + expect(serviceMock.create).toHaveBeenCalledTimes(2) + expect(serviceMock.create.mock.calls[0]?.[0].body.idempotency_key).toBe( + serviceMock.create.mock.calls[1]?.[0].body.idempotency_key, + ) }) it('keeps every start mode interactive without simulating backend success', async () => { @@ -460,6 +549,21 @@ describe('CreateKnowledgePage', () => { expect(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })).toBeDisabled() }) + it('disables upload before creating a space when direct upload is unavailable', () => { + navigationMock.startMode = 'upload' + systemFeaturesStateMock.uploadEnabled = false + + renderPage() + + expect(screen.getByRole('radio', { name: 'dataset.newKnowledge.startEmpty' })).toBeChecked() + const uploadFiles = screen.getByRole('radio', { name: 'dataset.newKnowledge.uploadFiles' }) + expect(uploadFiles).toBeDisabled() + expect(uploadFiles).toHaveAccessibleDescription( + 'dataset.newKnowledge.uploadFilesDescription dataset.cornerLabel.unavailable', + ) + expect(serviceMock.create).not.toHaveBeenCalled() + }) + it('continues from the upload mode after real creation succeeds', async () => { const user = userEvent.setup() navigationMock.startMode = 'upload' @@ -483,7 +587,7 @@ describe('CreateKnowledgePage', () => { ) expect(serviceMock.upload).toHaveBeenCalledWith({ body: { file: expect.objectContaining({ name: 'handbook.md' }) }, - params: { id: createdKnowledge.id }, + params: { control_space_id: createdKnowledge.control_space_id }, }) }) @@ -533,6 +637,12 @@ describe('CreateKnowledgePage', () => { const maxPages = screen.getByRole('spinbutton', { name: 'dataset.newKnowledge.maxPages' }) await user.clear(maxPages) await user.type(maxPages, '25') + await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.crawlOptions' })) + expect( + screen.getByText( + 'dataset.newKnowledge.includeSubpages: dataset.newKnowledge.booleanFalse · dataset.newKnowledge.maxPages: 25', + ), + ).toBeInTheDocument() await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) await waitFor(() => @@ -895,14 +1005,19 @@ describe('CreateKnowledgePage', () => { it('warns before leaving a partially created knowledge space', async () => { const user = userEvent.setup() - serviceMock.patchPolicy.mockRejectedValueOnce(new Error('policy update unavailable')) + navigationMock.startMode = 'upload' + serviceMock.upload.mockRejectedValueOnce(new Error('upload unavailable')) renderPage() + await user.upload( + screen.getByLabelText('dataset.newKnowledge.uploadFiles', { + selector: 'input[type="file"]', + }), + new File(['content'], 'handbook.md', { type: 'text/markdown' }), + ) await fillRequiredFields(user) await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.createTitle' })) - expect( - await screen.findByText('dataset.newKnowledge.permissionUpdateFailed'), - ).toBeInTheDocument() + expect(await screen.findByText('dataset.newKnowledge.documentUploadFailed')).toBeInTheDocument() await user.click(screen.getByRole('button', { name: 'common.operation.cancel' })) diff --git a/web/features/new-rag/__tests__/create-knowledge-workflow.spec.ts b/web/features/new-rag/__tests__/create-knowledge-workflow.spec.ts new file mode 100644 index 00000000000..823204588bf --- /dev/null +++ b/web/features/new-rag/__tests__/create-knowledge-workflow.spec.ts @@ -0,0 +1,131 @@ +import { createKnowledge, isDefinitiveCreationRejection } from '../create-knowledge-workflow' + +const serviceMock = vi.hoisted(() => ({ + createSpace: vi.fn(), + getDefaultModel: vi.fn(), +})) + +vi.mock('@/service/client', () => ({ + consoleClient: { + knowledgeFs: { + spaces: { + post: serviceMock.createSpace, + }, + }, + workspaces: { + current: { + defaultModel: { + get: serviceMock.getDefaultModel, + }, + }, + }, + }, +})) + +describe('createKnowledge', () => { + beforeEach(() => { + vi.clearAllMocks() + serviceMock.getDefaultModel.mockImplementation( + ({ query }: { query: { model_type: 'llm' | 'text-embedding' } }) => + Promise.resolve({ + data: { + model: query.model_type === 'llm' ? 'reasoning-model' : 'embedding-model', + provider: { + provider: + query.model_type === 'llm' + ? 'langgenius/openai/openai' + : 'langgenius/cohere/cohere', + }, + }, + }), + ) + serviceMock.createSpace.mockResolvedValue({ + control_space_id: 'control-space-1', + operation_id: 'operation-1', + state: 'provisioning', + }) + }) + + it('creates a control space with the new model intent and visibility', async () => { + const onCreated = vi.fn() + + await expect( + createKnowledge({ + description: 'Product docs', + idempotencyKey: '11111111-1111-4111-8111-111111111111', + name: 'Dify Product Docs', + onCreated, + visibility: 'all_team_members', + }), + ).resolves.toEqual({ + control_space_id: 'control-space-1', + operation_id: 'operation-1', + state: 'provisioning', + }) + + expect(serviceMock.createSpace).toHaveBeenCalledWith({ + body: { + description: 'Product docs', + embedding: { + model: 'embedding-model', + plugin_id: 'langgenius/cohere', + provider: 'cohere', + }, + idempotency_key: '11111111-1111-4111-8111-111111111111', + name: 'Dify Product Docs', + retrieval: { + default_mode: 'fast', + reasoning_model: { + model: 'reasoning-model', + plugin_id: 'langgenius/openai', + provider: 'openai', + }, + rerank: { enabled: false }, + score_threshold: { enabled: false, stage: 'mode-final' }, + top_k: 10, + }, + slug: expect.stringMatching(/^dify-product-docs-[a-z0-9]+$/), + visibility: 'all_team_members', + }, + }) + expect(onCreated).toHaveBeenCalledWith({ + control_space_id: 'control-space-1', + operation_id: 'operation-1', + state: 'provisioning', + }) + }) + + it('requires both default models before creating the control space', async () => { + serviceMock.getDefaultModel.mockImplementation( + ({ query }: { query: { model_type: 'llm' | 'text-embedding' } }) => + Promise.resolve( + query.model_type === 'llm' + ? { + data: { + model: 'reasoning-model', + provider: { provider: 'langgenius/openai/openai' }, + }, + } + : { data: null }, + ), + ) + + await expect( + createKnowledge({ + description: '', + idempotencyKey: '22222222-2222-4222-8222-222222222222', + name: '知识库', + onCreated: vi.fn(), + visibility: 'only_me', + }), + ).rejects.toMatchObject({ name: 'KnowledgeCreationError', stage: 'preflight' }) + expect(serviceMock.createSpace).not.toHaveBeenCalled() + }) +}) + +describe('isDefinitiveCreationRejection', () => { + it('only treats client authorization and validation failures as definitive', () => { + expect(isDefinitiveCreationRejection({ status: 422 })).toBe(true) + expect(isDefinitiveCreationRejection({ status: 503 })).toBe(false) + }) +}) diff --git a/web/features/new-rag/__tests__/document-detail-model.spec.ts b/web/features/new-rag/__tests__/document-detail-model.spec.ts index b4ba85c4ce4..28a1c8dfd74 100644 --- a/web/features/new-rag/__tests__/document-detail-model.spec.ts +++ b/web/features/new-rag/__tests__/document-detail-model.spec.ts @@ -2,7 +2,7 @@ import type { DocumentRevisionChunk, LogicalDocument, LogicalDocumentRevision, -} from '@dify/contracts/knowledge-fs/types.gen' +} from '../document-models' import { buildDocumentChunkTree, chunkCharacterCount, diff --git a/web/features/new-rag/__tests__/document-detail-page.spec.tsx b/web/features/new-rag/__tests__/document-detail-page.spec.tsx index d711db768b3..1dd57deb4ee 100644 --- a/web/features/new-rag/__tests__/document-detail-page.spec.tsx +++ b/web/features/new-rag/__tests__/document-detail-page.spec.tsx @@ -1,19 +1,30 @@ import type { - BulkDocumentReindexResult, DocumentProcessingTask, DocumentRevisionChunk, LogicalDocument, LogicalDocumentRevision, -} from '@dify/contracts/knowledge-fs/types.gen' +} from '../document-models' import { act, fireEvent, screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import copy from 'copy-to-clipboard' import { renderWithNuqs as render } from '@/test/nuqs-testing' import { DocumentDetailPage } from '../document-detail-page' +type BulkDocumentReindexResult = { + bulkJobId: string + items: Array<{ + asset?: unknown + compilationJob?: unknown + documentId?: string + status: 'not_found' | 'queued' + statusUrl?: string + }> + total: number +} + type InfiniteOptions = { enabled?: boolean - getNextPageParam: (lastPage: { nextCursor?: string }) => string | undefined + getNextPageParam: (lastPage: { next_cursor?: string | null }) => string | null | undefined input: (pageParam: string | null) => unknown initialPageParam: string | null queryKind: 'chunks' | 'revisions' | 'tasks' @@ -31,18 +42,6 @@ const documentQuery = vi.hoisted(() => ({ refetch: vi.fn(), })) -const taskSnapshotQuery = vi.hoisted(() => ({ - data: undefined as DocumentProcessingTask | undefined, - error: null as unknown, - refetch: vi.fn(), -})) - -const submissionTasksQuery = vi.hoisted(() => ({ - data: undefined as { items: DocumentProcessingTask[] } | undefined, - error: null as unknown, - refetch: vi.fn(), -})) - const revisionsQuery = vi.hoisted(() => ({ data: undefined as | { pages: Array<{ items: LogicalDocumentRevision[]; nextCursor?: string }> } @@ -92,10 +91,61 @@ const reindexMutation = vi.hoisted(() => ({ mutateAsync: vi.fn() })) const queryClient = vi.hoisted(() => ({ invalidateQueries: vi.fn(), removeQueries: vi.fn(), - setQueryData: vi.fn(), })) const toastState = vi.hoisted(() => ({ error: vi.fn(), info: vi.fn(), success: vi.fn() })) const virtualizerState = vi.hoisted(() => ({ scrollToIndex: vi.fn() })) +const revisionApiResponse = vi.hoisted( + () => (revision: Exclude) => ({ + activated_at: revision.activatedAt ?? null, + content_hash: revision.contentHash, + created_at: revision.createdAt, + document_asset_id: revision.documentAssetId, + document_asset_version: revision.documentAssetVersion, + document_id: revision.documentId, + knowledge_space_id: revision.knowledgeSpaceId, + mime_type: revision.mimeType, + revision: revision.revision, + size_bytes: revision.sizeBytes, + state: revision.state, + }), +) +const chunkApiResponse = vi.hoisted(() => (item: DocumentRevisionChunk) => ({ + created_at: item.createdAt, + document_id: item.documentId, + document_revision: item.documentRevision, + enabled: item.enabled, + id: item.id, + knowledge_space_id: item.knowledgeSpaceId, + ordinal: item.ordinal, + parent_chunk_id: item.parentChunkId ?? null, + text: item.text, + token_count: item.tokenCount, + user_metadata: item.userMetadata, +})) +const taskApiResponse = vi.hoisted(() => (item: DocumentProcessingTask) => ({ + can_cancel: item.canCancel ?? true, + can_retry: item.canRetry ?? item.state === 'failed', + completed_at: item.completedAt ?? null, + created_at: item.createdAt, + document_id: item.documentId, + document_revision: item.documentRevision, + error_code: item.errorCode ?? null, + error_message: item.errorMessage ?? null, + id: item.id, + knowledge_space_id: item.knowledgeSpaceId, + operation: item.operation ?? 'document_processing', + progress_percent: item.progressPercent, + state: + item.state === 'succeeded' + ? 'completed' + : item.state === 'dispatch_pending' + ? 'queued' + : item.state === 'superseded' + ? 'canceled' + : item.state, + task_kind: item.taskKind ?? 'document', + updated_at: item.updatedAt, +})) const documentOptions = vi.hoisted(() => vi.fn((options: object) => ({ ...options, @@ -103,23 +153,6 @@ const documentOptions = vi.hoisted(() => queryKind: 'document', })), ) -const taskSnapshotOptions = vi.hoisted(() => - vi.fn((options: object) => ({ ...options, queryKind: 'task-snapshot' })), -) -const documentSubmissionTasksOptions = vi.hoisted(() => - vi.fn((options: object) => ({ - ...options, - queryKey: ['knowledge-fs', 'submission-tasks', 'space-1', 'document-1'], - queryKind: 'submission-tasks', - })), -) -const workspaceSubmissionTasksOptions = vi.hoisted(() => - vi.fn((options: object) => ({ - ...options, - queryKey: ['knowledge-fs', 'workspace-submission-tasks', 'space-1'], - queryKind: 'submission-tasks', - })), -) const revisionsOptions = vi.hoisted(() => vi.fn((options: Omit) => ({ ...options, @@ -141,13 +174,6 @@ const documentTasksOptions = vi.hoisted(() => queryKind: 'tasks', })), ) -const workspaceTasksOptions = vi.hoisted(() => - vi.fn((options: Omit) => ({ - ...options, - queryKey: ['knowledge-fs', 'workspace-tasks', 'space-1'], - queryKind: 'tasks', - })), -) vi.mock('jotai', async (importOriginal) => { const original = await importOriginal() @@ -197,22 +223,51 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { return { ...original, useInfiniteQuery: (options: InfiniteOptions) => { - if (options.queryKind === 'revisions') return revisionsQuery + if (options.queryKind === 'revisions') + return { + ...revisionsQuery, + data: revisionsQuery.data + ? { + pages: revisionsQuery.data.pages.map((page) => ({ + data: page.items.flatMap((revision) => + revision ? [revisionApiResponse(revision)] : [], + ), + next_cursor: page.nextCursor ?? null, + })), + } + : undefined, + } if ( options.queryKind === 'chunks' || ('queryKey' in options && Array.isArray(options.queryKey) && options.queryKey.includes('chunks')) ) - return chunksQuery - return tasksQuery + return { + ...chunksQuery, + data: chunksQuery.data + ? { + pages: chunksQuery.data.pages.map((page) => ({ + data: page.items.map(chunkApiResponse), + next_cursor: page.nextCursor ?? null, + })), + } + : undefined, + } + return { + ...tasksQuery, + data: tasksQuery.data + ? { + pages: tasksQuery.data.pages.map((page) => ({ + data: page.items.map(taskApiResponse), + next_cursor: page.nextCursor ?? null, + })), + } + : undefined, + } }, useMutation: () => reindexMutation, - useQuery: (options: { queryKind?: string }) => { - if (options.queryKind === 'task-snapshot') return taskSnapshotQuery - if (options.queryKind === 'submission-tasks') return submissionTasksQuery - return documentQuery - }, + useQuery: () => documentQuery, useQueryClient: () => queryClient, } }) @@ -220,33 +275,46 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { vi.mock('@/service/client', () => ({ consoleQuery: { knowledgeFs: { - getKnowledgeSpacesByIdDocumentsByDocumentIdRevisions: { - infiniteOptions: revisionsOptions, - key: () => ['knowledge-fs', 'revisions'], - }, - getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunks: { - infiniteOptions: chunksOptions, - key: () => ['knowledge-fs', 'chunks'], - }, - getKnowledgeSpacesByIdLogicalDocumentsByDocumentId: { - queryOptions: documentOptions, - key: () => ['knowledge-fs', 'document'], - }, - getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId: { - queryOptions: taskSnapshotOptions, - }, - getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasks: { - infiniteOptions: documentTasksOptions, - key: () => ['knowledge-fs', 'tasks'], - queryOptions: documentSubmissionTasksOptions, - }, - getKnowledgeSpacesByIdProcessingTasks: { - infiniteOptions: workspaceTasksOptions, - key: () => ['knowledge-fs', 'workspace-tasks'], - queryOptions: workspaceSubmissionTasksOptions, - }, - postKnowledgeSpacesByIdDocumentsBulkReindex: { - mutationOptions: () => ({}), + spaces: { + byControlSpaceId: { + backgroundTasks: { + get: { + infiniteOptions: documentTasksOptions, + key: () => ['knowledge-fs', 'tasks'], + }, + }, + documents: { + byDocumentId: { + revisions: { + byRevision: { + chunks: { + get: { + infiniteOptions: chunksOptions, + key: () => ['knowledge-fs', 'chunks'], + }, + }, + }, + get: { + infiniteOptions: revisionsOptions, + key: () => ['knowledge-fs', 'revisions'], + }, + }, + }, + reindex: { + post: { + mutationOptions: () => ({}), + }, + }, + }, + logicalDocuments: { + byDocumentId: { + get: { + queryOptions: documentOptions, + key: () => ['knowledge-fs', 'document'], + }, + }, + }, + }, }, }, }, @@ -363,10 +431,6 @@ describe('DocumentDetailPage', () => { tasksQuery.isFetchNextPageError = false tasksQuery.isFetchingNextPage = false tasksQuery.isPending = false - taskSnapshotQuery.data = undefined - taskSnapshotQuery.error = null - submissionTasksQuery.data = undefined - submissionTasksQuery.error = null permissionState.refresh.mockResolvedValue({ data: { dataset: { default_permission_keys: ['dataset.acl.edit'] } }, error: null, @@ -380,24 +444,28 @@ describe('DocumentDetailPage', () => { expect(documentOptions).toHaveBeenCalledWith( expect.objectContaining({ - input: { params: { documentId: 'document-1', id: 'space-1' } }, + input: { + params: { control_space_id: 'space-1', document_id: 'document-1' }, + }, retry: expect.any(Function), }), ) expect(infiniteInput(revisionsOptions.mock.lastCall?.[0])(null)).toEqual({ - params: { documentId: 'document-1', id: 'space-1' }, - query: { limit: 50 }, + params: { control_space_id: 'space-1', document_id: 'document-1' }, + query: {}, }) expect(infiniteInput(chunksOptions.mock.lastCall?.[0])('next')).toEqual({ - params: { documentId: 'document-1', id: 'space-1', revision: 3 }, - query: { cursor: 'next', limit: 100 }, + params: { + control_space_id: 'space-1', + document_id: 'document-1', + revision: 3, + }, + query: { cursor: 'next' }, }) expect(infiniteInput(documentTasksOptions.mock.lastCall?.[0])(null)).toEqual({ - params: { documentId: 'document-1', id: 'space-1' }, + params: { control_space_id: 'space-1' }, query: { limit: 100 }, }) - expect(workspaceTasksOptions).not.toHaveBeenCalled() - expect(workspaceSubmissionTasksOptions).not.toHaveBeenCalled() }) it('does not construct a chunks request while the document is loading', () => { @@ -602,8 +670,12 @@ describe('DocumentDetailPage', () => { screen.getByRole('combobox', { name: 'dataset.newKnowledge.documentRevision' }), ).toHaveTextContent('v2') expect(infiniteInput(chunksOptions.mock.lastCall?.[0])(null)).toEqual({ - params: { documentId: 'document-1', id: 'space-1', revision: 2 }, - query: { limit: 100 }, + params: { + control_space_id: 'space-1', + document_id: 'document-1', + revision: 2, + }, + query: {}, }) }) @@ -684,19 +756,31 @@ describe('DocumentDetailPage', () => { ).toBeEnabled() }) - it('polls only the discovered active task through the single-task contract', () => { + it('polls active work through the unified background-task contract', () => { tasksQuery.data = { pages: [{ items: [task({ state: 'running' })] }] } render() - expect(taskSnapshotOptions).toHaveBeenCalledWith( - expect.objectContaining({ - enabled: true, - input: { - params: { documentId: 'document-1', id: 'space-1', taskId: 'task-1' }, + const taskOptions = documentTasksOptions.mock.lastCall?.[0] as unknown as { + refetchInterval: (query: { + state: { + data?: { + pages: Array<{ + data: Array> + next_cursor: string | null + }> + } + } + }) => number | false + } + expect( + taskOptions.refetchInterval({ + state: { + data: { + pages: [{ data: [taskApiResponse(task({ state: 'running' }))], next_cursor: null }], + }, }, - refetchInterval: expect.any(Function), }), - ) + ).toBe(5000) expect(tasksQuery.refetch).not.toHaveBeenCalled() }) @@ -705,7 +789,7 @@ describe('DocumentDetailPage', () => { const rendered = render( , ) - taskSnapshotQuery.data = task({ state: 'succeeded' }) + tasksQuery.data = { pages: [{ items: [task({ state: 'succeeded' })] }] } rendered.rerender() await waitFor(() => expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(4)) @@ -849,7 +933,7 @@ describe('DocumentDetailPage', () => { expect(reindexMutation.mutateAsync).toHaveBeenCalledWith({ body: { documentIds: ['document-1'] }, - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, }) await waitFor(() => expect(queryClient.invalidateQueries).toHaveBeenCalled()) @@ -995,25 +1079,48 @@ describe('DocumentDetailPage', () => { await user.click(button) expect(reindexMutation.mutateAsync).toHaveBeenCalledOnce() - const discoveryOptions = documentSubmissionTasksOptions.mock.lastCall?.[0] as unknown as { + const discoveryOptions = documentTasksOptions.mock.lastCall?.[0] as unknown as { refetchInterval: (query: { - state: { data?: { items: DocumentProcessingTask[] }; error?: unknown } + state: { + data?: { + pages: Array<{ + data: Array> + next_cursor: string | null + }> + } + } }) => number | false - retry: (failureCount: number, error: unknown) => boolean } - expect(discoveryOptions.refetchInterval({ state: { data: { items: [] } } })).toBe(2000) expect( discoveryOptions.refetchInterval({ - state: { data: { items: [task({ documentRevision: 5 })] } }, + state: { + data: { + pages: [ + { + data: [ + taskApiResponse(task({ documentRevision: 4, id: 'old-failed', state: 'failed' })), + ], + next_cursor: null, + }, + ], + }, + }, }), - ).toBe(false) + ).toBe(2000) expect( discoveryOptions.refetchInterval({ - state: { data: { items: [] }, error: { status: 403 } }, + state: { + data: { + pages: [ + { + data: [taskApiResponse(task({ documentRevision: 5 }))], + next_cursor: null, + }, + ], + }, + }, }), - ).toBe(false) - expect(discoveryOptions.retry(0, { status: 403 })).toBe(false) - expect(discoveryOptions.retry(0, { status: 404 })).toBe(false) + ).toBe(5000) }) it('keeps submission protection while a delayed status recheck is unresolved', async () => { @@ -1028,9 +1135,7 @@ describe('DocumentDetailPage', () => { }), ) try { - const rendered = render( - , - ) + render() const reindexButton = screen.getByRole('button', { name: 'dataset.newKnowledge.reindexDocument', }) @@ -1039,21 +1144,10 @@ describe('DocumentDetailPage', () => { await Promise.resolve() await Promise.resolve() }) - submissionTasksQuery.error = new Error('submission discovery failed') - rendered.rerender() await act(() => vi.advanceTimersByTimeAsync(30000)) const alert = screen.getByRole('alert') expect(alert).toHaveTextContent('dataset.newKnowledge.documentReindexConfirmationDelayed') - const timedOutDiscoveryOptions = documentSubmissionTasksOptions.mock - .lastCall?.[0] as unknown as { - refetchInterval: (query: { - state: { data?: { items: DocumentProcessingTask[] }; error?: unknown } - }) => number | false - } - expect(timedOutDiscoveryOptions.refetchInterval({ state: { data: { items: [] } } })).toBe( - false, - ) fireEvent.click( within(alert).getByRole('button', { name: 'dataset.newKnowledge.checkReindexStatus', @@ -1061,7 +1155,7 @@ describe('DocumentDetailPage', () => { ) expect(reindexButton).toHaveAttribute('data-disabled') expect(reindexMutation.mutateAsync).toHaveBeenCalledOnce() - expect(submissionTasksQuery.refetch).toHaveBeenCalledOnce() + expect(tasksQuery.refetch).toHaveBeenCalledOnce() await act(async () => { finishTaskRefresh?.() @@ -1081,14 +1175,30 @@ describe('DocumentDetailPage', () => { }) expect(reindexMutation.mutateAsync).toHaveBeenCalledTimes(2) expect(screen.getByRole('heading', { level: 1 })).toHaveFocus() - const discoveryOptions = documentSubmissionTasksOptions.mock.lastCall?.[0] as unknown as { + const discoveryOptions = documentTasksOptions.mock.lastCall?.[0] as unknown as { refetchInterval: (query: { - state: { data?: { items: DocumentProcessingTask[] } } + state: { + data?: { + pages: Array<{ + data: Array> + next_cursor: string | null + }> + } + } }) => number | false } expect( discoveryOptions.refetchInterval({ - state: { data: { items: [task({ documentRevision: 4, state: 'failed' })] } }, + state: { + data: { + pages: [ + { + data: [taskApiResponse(task({ documentRevision: 4, state: 'failed' }))], + next_cursor: null, + }, + ], + }, + }, }), ).toBe(2000) } finally { @@ -1134,22 +1244,29 @@ describe('DocumentDetailPage', () => { await Promise.resolve() }) - const discoveryOptions = documentSubmissionTasksOptions.mock.lastCall?.[0] as unknown as { - refetchInterval: (query: { - state: { data?: { items: DocumentProcessingTask[] }; error?: unknown } - }) => number | false + tasksQuery.data = { + pages: [ + { + items: [ + task({ + documentRevision: 5, + id: 'late-first', + state: 'succeeded', + }), + ], + }, + ], } + rendered.rerender() expect( - discoveryOptions.refetchInterval({ - state: { data: { items: [task({ documentRevision: 5, id: 'late-first' })] } }, - }), - ).toBe(2000) + screen.getByRole('button', { name: 'dataset.newKnowledge.reindexDocument' }), + ).toHaveAttribute('data-disabled') } finally { vi.useRealTimers() } }) - it('stops first-page submission discovery when task history observes the new task', async () => { + it('uses active-task polling after the unified task list observes the new task', async () => { const user = userEvent.setup() const rendered = render( , @@ -1162,61 +1279,82 @@ describe('DocumentDetailPage', () => { } rendered.rerender() - expect(documentSubmissionTasksOptions.mock.lastCall?.[0]).toMatchObject({ enabled: false }) + const taskOptions = documentTasksOptions.mock.lastCall?.[0] as unknown as { + refetchInterval: (query: { + state: { + data?: { + pages: Array<{ + data: Array> + next_cursor: string | null + }> + } + } + }) => number | false + } + expect( + taskOptions.refetchInterval({ + state: { + data: { + pages: [ + { + data: [taskApiResponse(task({ documentRevision: 4, state: 'running' }))], + next_cursor: null, + }, + ], + }, + }, + }), + ).toBe(5000) }) - it('stops snapshot polling and distrusts stale active task data after 403 or 404', async () => { - tasksQuery.data = { pages: [{ items: [task({ state: 'running' })] }] } - taskSnapshotQuery.error = { status: 404 } + it('surfaces unified task-list authorization failures and blocks re-indexing', () => { + tasksQuery.data = undefined + tasksQuery.error = { status: 403 } const rendered = render( , ) - expect(screen.queryByRole('status')).toBeNull() - const snapshotOptions = taskSnapshotOptions.mock.lastCall?.[0] as { - refetchInterval: (query: { - state: { data?: DocumentProcessingTask; error?: unknown } - }) => number | false - } - expect(snapshotOptions.refetchInterval({ state: { error: { status: 404 } } })).toBe(false) - await waitFor(() => expect(queryClient.invalidateQueries).toHaveBeenCalled()) - - queryClient.invalidateQueries.mockClear() - taskSnapshotQuery.error = { status: 403 } - rendered.rerender() expect(screen.getByRole('alert')).toHaveTextContent( 'dataset.newKnowledge.tasksErrorDescription', ) expect( screen.getByRole('button', { name: 'dataset.newKnowledge.reindexDocument' }), ).toHaveAttribute('data-disabled') + const taskOptions = documentTasksOptions.mock.lastCall?.[0] as unknown as { + refetchInterval: (query: { + state: { + data?: { + pages: Array<{ + data: Array> + next_cursor: string | null + }> + } + error?: unknown + } + }) => number | false + } + expect(taskOptions.refetchInterval({ state: { error: { status: 403 } } })).toBe(false) + + tasksQuery.error = { status: 404 } + rendered.rerender() + expect(screen.getByRole('alert')).toHaveTextContent( + 'dataset.newKnowledge.tasksErrorDescription', + ) }) - it('clears both task caches when a submission-discovered task snapshot returns 404', async () => { - submissionTasksQuery.data = { items: [task({ state: 'running' })] } - taskSnapshotQuery.error = { status: 404 } - + it('recovers task state directly from the unified task list', () => { + tasksQuery.data = undefined + tasksQuery.error = { status: 404 } const rendered = render( , ) - await waitFor(() => expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(2)) - expect(queryClient.invalidateQueries).toHaveBeenCalledWith({ - queryKey: ['knowledge-fs', 'tasks', 'space-1', 'document-1'], - }) - expect(queryClient.invalidateQueries).toHaveBeenCalledWith({ - queryKey: ['knowledge-fs', 'submission-tasks', 'space-1', 'document-1'], - }) - expect(queryClient.setQueryData).toHaveBeenCalledTimes(2) - - submissionTasksQuery.data = { items: [task({ id: 'missing-task-2', state: 'running' })] } + tasksQuery.error = null + tasksQuery.data = { pages: [{ items: [task({ state: 'running' })] }] } rendered.rerender() - await waitFor(() => expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(4)) - expect(queryClient.setQueryData).toHaveBeenCalledTimes(4) - - submissionTasksQuery.data = { items: [task({ state: 'running' })] } - rendered.rerender() - await waitFor(() => expect(queryClient.setQueryData).toHaveBeenCalledTimes(4)) + expect(screen.getByRole('status')).toHaveTextContent( + 'dataset.newKnowledge.documentReindexProgress:{"progress":"45"}', + ) }) it('refreshes stale detail and task-list caches for a newer terminal task on revisit', async () => { diff --git a/web/features/new-rag/__tests__/document-model.spec.ts b/web/features/new-rag/__tests__/document-model.spec.ts index 7b97acd7efc..72d28c33825 100644 --- a/web/features/new-rag/__tests__/document-model.spec.ts +++ b/web/features/new-rag/__tests__/document-model.spec.ts @@ -1,7 +1,4 @@ -import type { - DocumentProcessingTask, - LogicalDocument, -} from '@dify/contracts/knowledge-fs/types.gen' +import type { DocumentProcessingTask, LogicalDocument } from '../document-models' import { documentDisplayStatus, newestTaskByDocument, diff --git a/web/features/new-rag/__tests__/documents-page.spec.tsx b/web/features/new-rag/__tests__/documents-page.spec.tsx index 53368914ccb..49cc4e8f580 100644 --- a/web/features/new-rag/__tests__/documents-page.spec.tsx +++ b/web/features/new-rag/__tests__/documents-page.spec.tsx @@ -1,8 +1,5 @@ -import type { - DocumentProcessingTask, - LogicalDocument, - Source, -} from '@dify/contracts/knowledge-fs/types.gen' +import type { DocumentProcessingTask, LogicalDocument } from '../document-models' +import type { Source } from '../source-models' import { hashKey } from '@tanstack/react-query' import { act, fireEvent, screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' @@ -12,7 +9,7 @@ import { TaskEventObserver } from '../task-event-observer' type InfiniteOptions = { enabled?: boolean - getNextPageParam: (lastPage: { nextCursor?: string }) => string | undefined + getNextPageParam: (lastPage: { next_cursor?: string | null }) => string | null | undefined input: (pageParam: string | null) => unknown initialPageParam: string | null queryKind: 'documents' | 'sources' | 'tasks' @@ -93,6 +90,12 @@ const queryClient = vi.hoisted(() => ({ })) const streamProcessingTaskEvents = vi.hoisted(() => vi.fn()) const getTaskSnapshot = vi.hoisted(() => vi.fn()) +const taskSnapshotRequestState = vi.hoisted(() => ({ index: 0 })) +const rawQueryDataCache = vi.hoisted(() => ({ + documents: new WeakMap(), + sources: new WeakMap(), + tasks: new WeakMap(), +})) const permissionStateMock = vi.hoisted(() => ({ datasetAtom: Symbol('datasetDefaultPermissionKeysAtom'), datasetKeys: ['dataset.acl.edit'], @@ -107,12 +110,84 @@ const permissionStateMock = vi.hoisted(() => ({ refreshAfterDenial: vi.fn(), refreshAfterDenialAtom: Symbol('refreshWorkspacePermissionKeysAfterMutationDenialAtom'), })) +const systemFeaturesStateMock = vi.hoisted(() => ({ + atom: Symbol('knowledgeFsUploadEnabledAtom'), + uploadEnabled: true, +})) const toastMock = vi.hoisted(() => ({ error: vi.fn(), info: vi.fn(), success: vi.fn(), warning: vi.fn(), })) +const revisionApiResponse = vi.hoisted( + () => (revision: NonNullable) => ({ + activated_at: revision.activatedAt ?? null, + content_hash: revision.contentHash, + created_at: revision.createdAt, + document_asset_id: revision.documentAssetId, + document_asset_version: revision.documentAssetVersion, + document_id: revision.documentId, + knowledge_space_id: revision.knowledgeSpaceId, + mime_type: revision.mimeType, + revision: revision.revision, + size_bytes: revision.sizeBytes, + state: revision.state, + }), +) +const documentApiResponse = vi.hoisted(() => (item: LogicalDocument) => ({ + active: item.active ? revisionApiResponse(item.active) : null, + active_revision: item.activeRevision ?? null, + created_at: item.createdAt, + id: item.id, + knowledge_space_id: item.knowledgeSpaceId, + provider_item_id: item.providerItemId ?? null, + row_version: item.rowVersion, + source_id: item.sourceId ?? null, + status: item.status, + title: item.title, + updated_at: item.updatedAt, + user_metadata: item.userMetadata, +})) +const taskApiResponse = vi.hoisted(() => (item: DocumentProcessingTask) => ({ + can_cancel: item.canCancel ?? true, + can_retry: item.canRetry ?? item.state === 'failed', + completed_at: item.completedAt ?? null, + created_at: item.createdAt, + document_id: item.documentId, + document_revision: item.documentRevision, + error_code: item.errorCode ?? null, + error_message: item.errorMessage ?? null, + id: item.id, + knowledge_space_id: item.knowledgeSpaceId, + operation: item.operation ?? 'document_processing', + progress_percent: item.progressPercent, + state: + item.state === 'succeeded' + ? 'completed' + : item.state === 'dispatch_pending' + ? 'queued' + : item.state === 'superseded' + ? 'canceled' + : item.state, + task_kind: item.taskKind ?? 'document', + updated_at: item.updatedAt, +})) +const sourceApiResponse = vi.hoisted(() => (item: Source) => ({ + connection_id: item.connectionId ?? null, + created_at: item.createdAt, + credential_configured: item.credentialConfigured ?? null, + id: item.id, + knowledge_space_id: item.knowledgeSpaceId, + metadata: item.metadata, + name: item.name, + permission_scope: item.permissionScope ?? [], + status: item.status, + type: item.type, + updated_at: item.updatedAt, + uri: item.uri, + version: item.version ?? 1, +})) vi.mock('@/context/permission-state', () => ({ datasetDefaultPermissionKeysAtom: permissionStateMock.datasetAtom, @@ -123,6 +198,10 @@ vi.mock('@/context/permission-state', () => ({ workspacePermissionKeysLoadingAtom: permissionStateMock.loadingAtom, })) +vi.mock('@/context/system-features-state', () => ({ + knowledgeFsUploadEnabledAtom: systemFeaturesStateMock.atom, +})) + vi.mock('jotai', async (importOriginal) => { const original = await importOriginal() return { @@ -132,6 +211,7 @@ vi.mock('jotai', async (importOriginal) => { if (atom === permissionStateMock.errorAtom) return permissionStateMock.error if (atom === permissionStateMock.fetchingAtom) return permissionStateMock.fetching if (atom === permissionStateMock.loadingAtom) return permissionStateMock.loading + if (atom === systemFeaturesStateMock.atom) return systemFeaturesStateMock.uploadEnabled return original.useAtomValue(atom as Parameters[0]) }, useSetAtom: (atom: unknown) => @@ -159,21 +239,100 @@ const sourcesInfiniteOptions = vi.hoisted(() => vi.fn((options: Omit) => ({ ...options, queryKind: 'sources' })), ) +function rawDocumentQueryData(data: NonNullable): { + pages: Array<{ data: Array>; next_cursor: string | null }> +} { + const cached = rawQueryDataCache.documents.get(data) + if (cached) return cached as ReturnType + const mapped = { + pages: data.pages.map((page) => ({ + data: page.items.map(documentApiResponse), + next_cursor: page.nextCursor ?? null, + })), + } + rawQueryDataCache.documents.set(data, mapped) + return mapped +} + +function rawSourceQueryData(data: NonNullable): { + pages: Array<{ data: Array>; next_cursor: string | null }> +} { + const cached = rawQueryDataCache.sources.get(data) + if (cached) return cached as ReturnType + const mapped = { + pages: data.pages.map((page) => ({ + data: page.items.map(sourceApiResponse), + next_cursor: page.nextCursor ?? null, + })), + } + rawQueryDataCache.sources.set(data, mapped) + return mapped +} + +function rawTaskQueryData(data: NonNullable): { + pages: Array<{ data: Array>; next_cursor: string | null }> +} { + const cached = rawQueryDataCache.tasks.get(data) + if (cached) return cached as ReturnType + const mapped = { + pages: data.pages.map((page) => ({ + data: page.items.map(taskApiResponse), + next_cursor: page.nextCursor ?? null, + })), + } + rawQueryDataCache.tasks.set(data, mapped) + return mapped +} + vi.mock('@tanstack/react-query', async (importOriginal) => { const original = await importOriginal() return { ...original, useInfiniteQuery: (options: InfiniteOptions) => { - if (options.queryKind === 'documents') return documentsQuery - if (options.queryKind === 'sources') return sourcesQuery - return tasksQuery + if (options.queryKind === 'documents') + return { + ...documentsQuery, + data: documentsQuery.data ? rawDocumentQueryData(documentsQuery.data) : undefined, + } + if (options.queryKind === 'sources') + return { + ...sourcesQuery, + data: sourcesQuery.data ? rawSourceQueryData(sourcesQuery.data) : undefined, + } + return { + ...tasksQuery, + data: tasksQuery.data ? rawTaskQueryData(tasksQuery.data) : undefined, + } }, useMutation: (options: { - mutationKind: 'bulk-upload' | 'cancel' | 'reindex' | 'retry' | 'upload' + mutationFn?: (input: DocumentProcessingTask) => Promise + mutationKind?: 'bulk-upload' | 'cancel' | 'reindex' | 'retry' | 'upload' }) => { + if (options.mutationFn) + return { + mutateAsync: options.mutationFn, + } if (options.mutationKind === 'cancel') return cancelMutation if (options.mutationKind === 'retry') return retryMutation - if (options.mutationKind === 'reindex') return reindexMutation + if (options.mutationKind === 'reindex') + return { + mutateAsync: async (input: unknown) => { + const result = await reindexMutation.mutateAsync(input) + return { + ...result, + items: result.items.map( + (item: { + documentId?: string + document_id?: string + status: 'not_found' | 'queued' + }) => ({ + ...item, + document_id: item.document_id ?? item.documentId ?? null, + }), + ), + } + }, + } if (options.mutationKind === 'bulk-upload') return bulkUploadMutation return uploadMutation }, @@ -184,43 +343,100 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { vi.mock('@/service/client', () => ({ consoleClient: { knowledgeFs: { - getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId: getTaskSnapshot, + spaces: { + byControlSpaceId: { + backgroundTasks: { + byTaskKind: { + byTaskId: { + cancel: { + post: async (input: unknown) => + taskApiResponse(await cancelMutation.mutateAsync(input)), + }, + retry: { + post: async (input: unknown) => + taskApiResponse(await retryMutation.mutateAsync(input)), + }, + }, + }, + get: async (input: unknown, options?: unknown) => { + const allTasks = tasksQuery.data?.pages.flatMap((page) => page.items) ?? [] + const requestedTask = allTasks[taskSnapshotRequestState.index % allTasks.length] + taskSnapshotRequestState.index += 1 + const snapshot = await getTaskSnapshot( + requestedTask + ? { + params: { + documentId: requestedTask.documentId, + id: requestedTask.knowledgeSpaceId, + taskId: requestedTask.id, + }, + } + : input, + options, + ) + return { + data: snapshot ? [taskApiResponse(snapshot)] : [], + next_cursor: null, + } + }, + }, + }, + }, }, }, consoleQuery: { knowledgeFs: { - deleteKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskId: { - mutationOptions: () => ({ mutationKind: 'cancel' }), - }, - getKnowledgeSpacesByIdLogicalDocuments: { - infiniteOptions: documentsInfiniteOptions, - key: () => ['knowledge-fs', 'documents'], - }, - getKnowledgeSpacesByIdProcessingTasks: { - infiniteOptions: tasksInfiniteOptions, - key: () => ['knowledge-fs', 'tasks'], - }, - getKnowledgeSpacesByIdSources: { - infiniteOptions: sourcesInfiniteOptions, - key: () => ['knowledge-fs', 'sources'], - }, - postKnowledgeSpacesByIdDocuments: { - mutationOptions: () => ({ mutationKind: 'upload' }), - }, - postKnowledgeSpacesByIdDocumentsBulk: { - mutationOptions: () => ({ mutationKind: 'bulk-upload' }), - }, - postKnowledgeSpacesByIdDocumentsBulkReindex: { - mutationOptions: () => ({ mutationKind: 'reindex' }), - }, - postKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdRetry: { - mutationOptions: () => ({ mutationKind: 'retry' }), + spaces: { + byControlSpaceId: { + backgroundTasks: { + get: { + infiniteOptions: tasksInfiniteOptions, + key: () => ['knowledge-fs', 'tasks'], + }, + }, + documents: { + reindex: { + post: { + mutationOptions: () => ({ mutationKind: 'reindex' }), + }, + }, + }, + logicalDocuments: { + get: { + infiniteOptions: documentsInfiniteOptions, + key: () => ['knowledge-fs', 'documents'], + }, + }, + sources: { + get: { + infiniteOptions: sourcesInfiniteOptions, + key: () => ['knowledge-fs', 'sources'], + }, + }, + }, }, }, }, })) vi.mock('../services/processing-task-events', () => ({ streamProcessingTaskEvents })) +vi.mock('../knowledge-fs-upload', () => ({ + uploadKnowledgeFsDocuments: async ( + knowledgeSpaceId: string, + uploads: Array<{ file: File; id: string }>, + ) => { + const files = uploads.map(({ file }) => file) + if (files.length === 1) + return uploadMutation.mutateAsync({ + body: { file: files[0] }, + params: { control_space_id: knowledgeSpaceId }, + }) + return bulkUploadMutation.mutateAsync({ + body: { files }, + params: { control_space_id: knowledgeSpaceId }, + }) + }, +})) const document = (overrides: Partial = {}): LogicalDocument => ({ active: { @@ -313,7 +529,9 @@ const source = (overrides: Partial = {}): Source => ({ describe('DocumentsPage', () => { beforeEach(() => { vi.clearAllMocks() + systemFeaturesStateMock.uploadEnabled = true queryCacheListeners.clear() + taskSnapshotRequestState.index = 0 queryClient.cancelQueries.mockResolvedValue(undefined) queryClient.invalidateQueries.mockResolvedValue(undefined) documentsQuery.data = { pages: [{ items: [] }] } @@ -424,24 +642,24 @@ describe('DocumentsPage', () => { const taskOptions = tasksInfiniteOptions.mock.lastCall?.[0] const sourceOptions = sourcesInfiniteOptions.mock.lastCall?.[0] expect(documentOptions?.input(null)).toEqual({ - params: { id: 'space-1' }, - query: { limit: 50 }, + params: { control_space_id: 'space-1' }, + query: {}, }) expect(documentOptions?.input('next')).toEqual({ - params: { id: 'space-1' }, - query: { cursor: 'next', limit: 50 }, + params: { control_space_id: 'space-1' }, + query: { cursor: 'next' }, }) - expect(documentOptions?.getNextPageParam({ nextCursor: 'next' })).toBe('next') + expect(documentOptions?.getNextPageParam({ next_cursor: 'next' })).toBe('next') expect(taskOptions?.input(null)).toEqual({ - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, query: { limit: 100 }, }) - expect(taskOptions?.getNextPageParam({ nextCursor: 'next' })).toBe('next') + expect(taskOptions?.getNextPageParam({ next_cursor: 'next' })).toBe('next') expect(sourceOptions?.input(null)).toEqual({ - params: { id: 'space-1' }, - query: { limit: 100 }, + params: { control_space_id: 'space-1' }, + query: {}, }) - expect(sourceOptions?.getNextPageParam({ nextCursor: 'next' })).toBe('next') + expect(sourceOptions?.getNextPageParam({ next_cursor: 'next' })).toBe('next') expect(screen.getByRole('status', { name: 'appApi.loading' })).toBeInTheDocument() }) @@ -588,6 +806,19 @@ describe('DocumentsPage', () => { expect(dataTransfer.dropEffect).toBe('copy') }) + it('keeps direct-upload actions unavailable until the deployment is verified', () => { + systemFeaturesStateMock.uploadEnabled = false + + render() + + expect(screen.queryByLabelText('dataset.newKnowledge.uploadDocuments')).not.toBeInTheDocument() + const addDocument = screen.getByRole('button', { + name: 'dataset.newKnowledge.addDocument', + }) + expect(addDocument).toBeDisabled() + expect(addDocument).toHaveAccessibleDescription('dataset.cornerLabel.unavailable') + }) + it('removes the empty-state drop affordance when uploads are unavailable', () => { permissionStateMock.datasetKeys = ['dataset.acl.readonly'] @@ -744,7 +975,7 @@ describe('DocumentsPage', () => { await user.upload(input, new File(['one'], 'one.md', { type: 'text/markdown' })) expect(uploadMutation.mutateAsync).toHaveBeenCalledWith({ body: { file: expect.any(File) }, - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, }) await user.upload(input, [ @@ -753,7 +984,7 @@ describe('DocumentsPage', () => { ]) expect(bulkUploadMutation.mutateAsync).toHaveBeenCalledWith({ body: { files: [expect.any(File), expect.any(File)] }, - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, }) expect(queryClient.invalidateQueries).toHaveBeenCalled() const documentInvalidation = queryClient.invalidateQueries.mock.calls.find( @@ -763,7 +994,7 @@ describe('DocumentsPage', () => { documentInvalidation?.predicate({ queryKey: [ ['console', 'knowledgeFs', 'getKnowledgeSpacesByIdLogicalDocuments'], - { input: { params: { id: 'space-1' } }, type: 'infinite' }, + { input: { params: { control_space_id: 'space-1' } }, type: 'infinite' }, ], }), ).toBe(true) @@ -771,7 +1002,7 @@ describe('DocumentsPage', () => { documentInvalidation?.predicate({ queryKey: [ ['console', 'knowledgeFs', 'getKnowledgeSpacesByIdLogicalDocuments'], - { input: { params: { id: 'space-2' } }, type: 'infinite' }, + { input: { params: { control_space_id: 'space-2' } }, type: 'infinite' }, ], }), ).toBe(false) @@ -791,7 +1022,7 @@ describe('DocumentsPage', () => { await waitFor(() => expect(uploadMutation.mutateAsync).toHaveBeenCalledWith({ body: { file: validFile }, - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, }), ) expect(bulkUploadMutation.mutateAsync).not.toHaveBeenCalled() @@ -816,68 +1047,28 @@ describe('DocumentsPage', () => { ) }) - it('reports partial and fully excluded bulk uploads from the contract result', async () => { + it('reports local exclusions and direct upload failures', async () => { const user = userEvent.setup() - bulkUploadMutation.mutateAsync - .mockResolvedValueOnce({ - accepted: 1, - bulkJobId: 'upload-partial', - excluded: 1, - items: [ - { - filename: 'too-large.pdf', - index: 1, - mimeType: 'application/pdf', - reason: 'file_too_large', - sizeBytes: 10_000, - status: 'excluded', - }, - ], - total: 2, - }) - .mockResolvedValueOnce({ - accepted: 0, - bulkJobId: 'upload-rejected', - excluded: 2, - items: [ - { - filename: 'one.md', - index: 0, - mimeType: 'text/markdown', - reason: 'quota_exceeded', - sizeBytes: 3, - status: 'excluded', - }, - { - filename: 'two.md', - index: 1, - mimeType: 'text/markdown', - reason: 'quota_exceeded', - sizeBytes: 3, - status: 'excluded', - }, - ], - total: 2, - }) render() const input = screen.getByLabelText('dataset.newKnowledge.uploadDocuments') + const oversizedFile = new File(['large'], 'too-large.pdf', { type: 'application/pdf' }) + Object.defineProperty(oversizedFile, 'size', { value: 16 * 1024 * 1024 }) await user.upload(input, [ new File(['one'], 'one.md', { type: 'text/markdown' }), - new File(['large'], 'too-large.pdf', { type: 'application/pdf' }), + oversizedFile, ]) expect(toastMock.warning).toHaveBeenCalledWith( 'dataset.newKnowledge.documentUploadPartial:{"accepted":1,"details":"too-large.pdf (dataset.newKnowledge.documentUploadExclusion.fileSize)","excluded":1}', ) queryClient.invalidateQueries.mockClear() + bulkUploadMutation.mutateAsync.mockRejectedValueOnce(new Error('quota exceeded')) await user.upload(input, [ new File(['one'], 'one.md', { type: 'text/markdown' }), new File(['two'], 'two.md', { type: 'text/markdown' }), ]) - expect(toastMock.error).toHaveBeenCalledWith( - 'dataset.newKnowledge.documentUploadRejected:{"details":"one.md (dataset.newKnowledge.documentUploadExclusion.quota); two.md (dataset.newKnowledge.documentUploadExclusion.quota)"}', - ) + expect(toastMock.error).toHaveBeenCalledWith('dataset.newKnowledge.documentUploadFailed') expect(queryClient.invalidateQueries).not.toHaveBeenCalled() }) @@ -1080,7 +1271,7 @@ describe('DocumentsPage', () => { taskCancellation?.predicate({ queryKey: [ ['console', 'knowledgeFs', 'getKnowledgeSpacesByIdProcessingTasks'], - { input: { params: { id: 'space-1' } }, type: 'infinite' }, + { input: { params: { control_space_id: 'space-1' } }, type: 'infinite' }, ], }), ).toBe(true) @@ -1088,7 +1279,7 @@ describe('DocumentsPage', () => { taskCancellation?.predicate({ queryKey: [ ['console', 'knowledgeFs', 'getKnowledgeSpacesByIdProcessingTasks'], - { input: { params: { id: 'space-2' } }, type: 'infinite' }, + { input: { params: { control_space_id: 'space-2' } }, type: 'infinite' }, ], }), ).toBe(false) @@ -1709,7 +1900,7 @@ describe('DocumentsPage', () => { expect(reindexMutation.mutateAsync).toHaveBeenCalledOnce() expect(reindexMutation.mutateAsync).toHaveBeenCalledWith({ body: { documentIds: ['one'] }, - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, }) await waitFor(() => expect(screen.getByRole('heading', { name: 'dataset.newKnowledge.documents' })).toHaveFocus(), @@ -1941,7 +2132,11 @@ describe('DocumentsPage', () => { expect(cancelMutation.mutateAsync).toHaveBeenCalledOnce() expect(cancelMutation.mutateAsync).toHaveBeenCalledWith({ - params: { documentId: 'document-1', id: 'space-1', taskId: 'running' }, + params: { + control_space_id: 'space-1', + task_id: 'running', + task_kind: 'document', + }, }) await act(async () => resolveCancel?.(task({ id: 'running', state: 'canceled' }))) expect(queryClient.invalidateQueries).toHaveBeenCalled() @@ -3906,7 +4101,20 @@ describe('DocumentsPage', () => { await act(async () => vi.advanceTimersByTime(5000)) expect(streamProcessingTaskEvents).toHaveBeenCalledTimes(12) const taskOptions = tasksInfiniteOptions.mock.lastCall?.[0] - expect(taskOptions?.refetchInterval).toBeUndefined() + expect( + taskOptions?.refetchInterval?.({ + state: { + data: { + pages: [ + { + data: [taskApiResponse(task({ id: 'active-0' }))], + next_cursor: null, + }, + ], + }, + }, + }), + ).toBe(5000) expect( screen.getByRole('button', { name: 'dataset.newKnowledge.tasksWithAttention:{"count":20}', diff --git a/web/features/new-rag/__tests__/knowledge-fs-upload.spec.ts b/web/features/new-rag/__tests__/knowledge-fs-upload.spec.ts new file mode 100644 index 00000000000..44a86aa9819 --- /dev/null +++ b/web/features/new-rag/__tests__/knowledge-fs-upload.spec.ts @@ -0,0 +1,154 @@ +import { uploadKnowledgeFsDocuments } from '../knowledge-fs-upload' + +const serviceMock = vi.hoisted(() => ({ + getSpace: vi.fn(), + issueCapability: vi.fn(), + smallFile: vi.fn(), +})) + +vi.mock('@/service/client', () => ({ + consoleClient: { + knowledgeFs: { + spaces: { + byControlSpaceId: { + get: serviceMock.getSpace, + uploadCapabilities: { + post: serviceMock.issueCapability, + }, + uploadSessions: { + byUploadSessionId: { + smallFile: { + post: serviceMock.smallFile, + }, + }, + }, + }, + }, + }, + }, +})) + +describe('uploadKnowledgeFsDocuments', () => { + beforeEach(() => { + vi.clearAllMocks() + serviceMock.getSpace.mockResolvedValue({ + knowledge_space_id: 'physical-space-1', + state: 'active', + }) + serviceMock.issueCapability.mockResolvedValue({ + direct_origin: 'https://knowledge-fs.example', + expires_at: '2026-07-27T12:00:00Z', + operation_id: 'createUploadSession', + token: 'capability-token', + }) + serviceMock.smallFile.mockResolvedValue({ + session: { id: 'session-1', mode: 'small_fallback', status: 'completed' }, + }) + vi.spyOn(globalThis.crypto.subtle, 'digest').mockResolvedValue(new Uint8Array(32).buffer) + }) + + afterEach(() => { + vi.restoreAllMocks() + vi.unstubAllGlobals() + }) + + it('creates a capability-bound session and uses the Dify small-file fallback', async () => { + const request = vi.fn().mockResolvedValue( + new Response( + JSON.stringify({ + session: { + id: 'session-1', + mode: 'small_fallback', + status: 'ready', + }, + }), + { + status: 201, + headers: { 'content-type': 'application/json' }, + }, + ), + ) + vi.stubGlobal('fetch', request) + const file = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + + await uploadKnowledgeFsDocuments('control-space-1', [{ file, id: 'upload-1' }]) + + expect(serviceMock.issueCapability).toHaveBeenCalledWith({ + body: { operation_id: 'createUploadSession' }, + params: { control_space_id: 'control-space-1' }, + }) + expect(request).toHaveBeenCalledWith( + 'https://knowledge-fs.example/knowledge-spaces/physical-space-1/upload-sessions', + expect.objectContaining({ + headers: { + Authorization: 'Bearer capability-token', + 'Content-Type': 'application/json', + }, + method: 'POST', + }), + ) + expect(serviceMock.smallFile).toHaveBeenCalledWith({ + body: { file }, + params: { + control_space_id: 'control-space-1', + upload_session_id: 'session-1', + }, + }) + }) + + it('resumes only the failed file after a partial multi-file upload', async () => { + const request = vi.fn(async (_url: string, init?: RequestInit) => { + const body = JSON.parse(String(init?.body)) as { fileName: string } + const sessionId = body.fileName === 'a.txt' ? 'session-a' : 'session-b' + return new Response( + JSON.stringify({ + session: { + id: sessionId, + mode: 'small_fallback', + status: 'ready', + }, + }), + { + status: 201, + headers: { 'content-type': 'application/json' }, + }, + ) + }) + vi.stubGlobal('fetch', request) + serviceMock.smallFile.mockImplementation( + ({ params }: { params: { upload_session_id: string } }) => { + if ( + params.upload_session_id === 'session-b' && + serviceMock.smallFile.mock.calls.filter( + ([call]) => call.params.upload_session_id === 'session-b', + ).length === 1 + ) + return Promise.reject(new Error('response lost')) + return Promise.resolve({ + session: { + id: params.upload_session_id, + mode: 'small_fallback', + status: 'completed', + }, + }) + }, + ) + const uploads = [ + { file: new File(['a'], 'a.txt', { type: 'text/plain' }), id: 'upload-a' }, + { file: new File(['b'], 'b.txt', { type: 'text/plain' }), id: 'upload-b' }, + ] + const progress = new Map() + + await expect(uploadKnowledgeFsDocuments('control-space-1', uploads, progress)).rejects.toThrow( + 'response lost', + ) + await expect( + uploadKnowledgeFsDocuments('control-space-1', uploads, progress), + ).resolves.toBeUndefined() + + expect(request).toHaveBeenCalledTimes(2) + expect(serviceMock.smallFile.mock.calls.map(([call]) => call.params.upload_session_id)).toEqual( + ['session-a', 'session-b', 'session-b'], + ) + }) +}) diff --git a/web/features/new-rag/__tests__/knowledge-space-shell.spec.tsx b/web/features/new-rag/__tests__/knowledge-space-shell.spec.tsx index 1836eb444e2..293882338a7 100644 --- a/web/features/new-rag/__tests__/knowledge-space-shell.spec.tsx +++ b/web/features/new-rag/__tests__/knowledge-space-shell.spec.tsx @@ -6,8 +6,8 @@ import { KnowledgeSpaceShell } from '../knowledge-space-shell' const queryMock = vi.hoisted(() => ({ data: undefined as | { - id: string - name: string + control_space_id: string + technical_summary: { name: string } } | undefined, error: null as unknown, @@ -37,8 +37,12 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { vi.mock('@/service/client', () => ({ consoleQuery: { knowledgeFs: { - getKnowledgeSpacesById: { - queryOptions: queryOptionsMock, + spaces: { + byControlSpaceId: { + get: { + queryOptions: queryOptionsMock, + }, + }, }, }, }, @@ -60,12 +64,17 @@ describe('KnowledgeSpaceShell', () => { render(content) - expect(queryOptionsMock).toHaveBeenCalledWith({ input: { params: { id: 'space-1' } } }) + expect(queryOptionsMock).toHaveBeenCalledWith({ + input: { params: { control_space_id: 'space-1' } }, + }) expect(screen.getByRole('status')).toBeInTheDocument() }) it('renders a refresh-safe header and route navigation when loaded', () => { - queryMock.data = { id: 'space-1', name: 'Support knowledge' } + queryMock.data = { + control_space_id: 'space-1', + technical_summary: { name: 'Support knowledge' }, + } render(source content) @@ -129,7 +138,10 @@ describe('KnowledgeSpaceShell', () => { it('marks Documents as the only current detail route', () => { pathnameMock.value = '/datasets/new/space-1/documents' - queryMock.data = { id: 'space-1', name: 'Support knowledge' } + queryMock.data = { + control_space_id: 'space-1', + technical_summary: { name: 'Support knowledge' }, + } render(document content) diff --git a/web/features/new-rag/__tests__/knowledge-view-switcher.spec.tsx b/web/features/new-rag/__tests__/knowledge-view-switcher.spec.tsx new file mode 100644 index 00000000000..dba125a2e8e --- /dev/null +++ b/web/features/new-rag/__tests__/knowledge-view-switcher.spec.tsx @@ -0,0 +1,38 @@ +import { render, screen, within } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { KnowledgeViewSwitcher } from '../components/knowledge-view-switcher' + +const guideStorageMock = vi.hoisted(() => ({ + dismissed: false, + setDismissed: vi.fn(), +})) + +vi.mock('@/features/new-rag/storage', () => ({ + useNewKnowledgeGuideDismissedValue: () => guideStorageMock.dismissed, + useSetNewKnowledgeGuideDismissed: () => guideStorageMock.setDismissed, +})) + +describe('KnowledgeViewSwitcher', () => { + beforeEach(() => { + vi.clearAllMocks() + guideStorageMock.dismissed = false + }) + + it('restores focus to the guide trigger when Escape closes the popover', async () => { + const user = userEvent.setup() + render() + + const trigger = screen.getByRole('button', { + name: 'dataset.newKnowledge.guideTitle', + }) + const guide = screen.getByRole('dialog', { + name: 'dataset.newKnowledge.guideTitle', + }) + within(guide).getByRole('button', { name: 'dataset.newKnowledge.gotIt' }).focus() + + await user.keyboard('{Escape}') + + expect(screen.queryByRole('dialog')).not.toBeInTheDocument() + expect(trigger).toHaveFocus() + }) +}) diff --git a/web/features/new-rag/__tests__/new-knowledge-list.spec.tsx b/web/features/new-rag/__tests__/new-knowledge-list.spec.tsx index fe0acf093cd..edeb40dc2b2 100644 --- a/web/features/new-rag/__tests__/new-knowledge-list.spec.tsx +++ b/web/features/new-rag/__tests__/new-knowledge-list.spec.tsx @@ -1,17 +1,31 @@ -import type { KnowledgeSpaceList } from '@dify/contracts/knowledge-fs/types.gen' import type { InfiniteData } from '@tanstack/react-query' import { screen, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { renderWithNuqs } from '@/test/nuqs-testing' import { NewKnowledgeList } from '../new-knowledge-list' +type KnowledgeSpaceList = { + items: Array<{ + createdAt: string + description?: string + iconRef?: string + id: string + name: string + revision: number + slug: string + tenantId: string + updatedAt: string + }> + nextCursor?: string +} + type ListKnowledgeSpacesInfiniteOptions = { - getNextPageParam: (lastPage: KnowledgeSpaceList) => string | undefined - initialPageParam: string | null + getNextPageParam: (lastPage: { has_more: boolean; page: number }) => number | undefined + initialPageParam: number input: (pageParam: unknown) => { query: { - cursor?: string limit: number + page: number } } } @@ -21,6 +35,28 @@ const externalApiPanelMock = vi.hoisted(() => ({ setOpen: vi.fn(), })) const toastInfoMock = vi.hoisted(() => vi.fn()) +const knowledgeSpaceApiResponse = vi.hoisted( + () => (space: KnowledgeSpaceList['items'][number]) => ({ + control_space_id: space.id, + created_at: space.createdAt, + knowledge_space_id: space.id, + owner_account_id: 'account-1', + permission_keys: ['knowledge_space_read'], + resource_version: space.revision, + state: 'active', + technical_status: 'available', + technical_summary: { + description: space.description ?? null, + icon: space.iconRef ?? null, + knowledge_space_id: space.id, + name: space.name, + revision: space.revision, + slug: space.slug, + }, + updated_at: space.updatedAt, + visibility: 'only_me', + }), +) vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { info: toastInfoMock }, @@ -69,7 +105,20 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { const original = await importOriginal() return { ...original, - useInfiniteQuery: () => queryMock, + useInfiniteQuery: () => ({ + ...queryMock, + data: queryMock.data + ? { + ...queryMock.data, + pages: queryMock.data.pages.map((page, index) => ({ + data: page.items.map(knowledgeSpaceApiResponse), + has_more: Boolean(page.nextCursor), + limit: 30, + page: index + 1, + })), + } + : undefined, + }), } }) @@ -92,8 +141,10 @@ vi.mock('@/context/permission-state', () => ({ vi.mock('@/service/client', () => ({ consoleQuery: { knowledgeFs: { - listKnowledgeSpaces: { - infiniteOptions: consoleQueryMock.infiniteOptions, + spaces: { + get: { + infiniteOptions: consoleQueryMock.infiniteOptions, + }, }, }, }, @@ -137,13 +188,13 @@ describe('NewKnowledgeList', () => { const options = consoleQueryMock.infiniteOptions.mock.calls.at(-1)?.[0] expect(options).toBeDefined() - expect(options?.initialPageParam).toBeNull() - expect(options?.input(null)).toEqual({ query: { limit: 30 } }) - expect(options?.input('next-page')).toEqual({ - query: { cursor: 'next-page', limit: 30 }, + expect(options?.initialPageParam).toBe(1) + expect(options?.input(1)).toEqual({ query: { limit: 30, page: 1 } }) + expect(options?.input(2)).toEqual({ + query: { limit: 30, page: 2 }, }) - expect(options?.getNextPageParam({ items: [], nextCursor: 'next-page' })).toBe('next-page') - expect(options?.getNextPageParam({ items: [] })).toBeUndefined() + expect(options?.getNextPageParam({ has_more: true, page: 1 })).toBe(2) + expect(options?.getNextPageParam({ has_more: false, page: 1 })).toBeUndefined() }) it('links real knowledge spaces to the new detail shell', () => { diff --git a/web/features/new-rag/__tests__/processing-task-events.spec.ts b/web/features/new-rag/__tests__/processing-task-events.spec.ts index f871acc9c76..7c43118d342 100644 --- a/web/features/new-rag/__tests__/processing-task-events.spec.ts +++ b/web/features/new-rag/__tests__/processing-task-events.spec.ts @@ -1,68 +1,67 @@ -import type { DocumentProcessingTaskEvent } from '@dify/contracts/knowledge-fs/types.gen' -import { withEventMeta } from '@orpc/client' import { beforeEach, describe, expect, it, vi } from 'vitest' import { streamProcessingTaskEvents } from '../services/processing-task-events' -const { mockStreamEvents } = vi.hoisted(() => ({ - mockStreamEvents: vi.fn(), +const { listBackgroundTasks } = vi.hoisted(() => ({ + listBackgroundTasks: vi.fn(), })) vi.mock('@/service/client', () => ({ consoleClient: { knowledgeFs: { - getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEvents: mockStreamEvents, + spaces: { + byControlSpaceId: { + backgroundTasks: { + get: listBackgroundTasks, + }, + }, + }, }, }, })) -async function* eventIterator(...events: DocumentProcessingTaskEvent[]) { - yield* events -} +const task = ( + state: 'completed' | 'failed' | 'queued' | 'running', + overrides: { documentId?: string; id?: string } = {}, +) => ({ + can_cancel: state === 'queued' || state === 'running', + can_retry: state === 'failed', + completed_at: state === 'completed' ? '2026-07-20T01:03:00Z' : null, + created_at: '2026-07-20T01:00:00Z', + document_id: overrides.documentId ?? 'document/1', + document_revision: 2, + error_code: state === 'failed' ? 'PROCESSING_FAILED' : null, + error_message: null, + id: overrides.id ?? 'task/1', + knowledge_space_id: 'space/1', + operation: 'document_processing', + progress_percent: state === 'completed' ? 100 : 45, + state, + task_kind: 'document', + updated_at: state === 'completed' ? '2026-07-20T01:03:00Z' : '2026-07-20T01:02:03Z', +}) describe('KnowledgeFS processing task events', () => { beforeEach(() => { vi.clearAllMocks() + vi.useRealTimers() }) - it('uses the generated streaming client and resumes from the last event id', async () => { - mockStreamEvents.mockResolvedValue( - eventIterator( - withEventMeta( - { - data: { - progressPercent: 45, - stage: 'parsed', - state: 'running', - updatedAt: '2026-07-20T01:02:03Z', - }, - event: 'progress', - }, - { id: 'task-1:2026-07-20T01:02:03Z' }, - ), - withEventMeta( - { - data: { state: 'succeeded' }, - event: 'terminal', - }, - { id: 'task-1:terminal' }, - ), - ), - ) + it('polls the unified background-task endpoint until the task is terminal', async () => { + vi.useFakeTimers() + listBackgroundTasks + .mockResolvedValueOnce({ data: [task('running')], next_cursor: null }) + .mockResolvedValueOnce({ data: [task('completed')], next_cursor: null }) const abortController = new AbortController() - - const events = [] - for await (const event of streamProcessingTaskEvents({ + const events = streamProcessingTaskEvents({ documentId: 'document/1', knowledgeSpaceId: 'space/1', - lastEventId: 'task-1:previous', signal: abortController.signal, taskId: 'task/1', - })) { - events.push(event) - } + }) - expect(events).toEqual([ - { + await expect(events.next()).resolves.toEqual({ + done: false, + value: { data: { progressPercent: 45, stage: 'parsed', @@ -70,46 +69,92 @@ describe('KnowledgeFS processing task events', () => { updatedAt: '2026-07-20T01:02:03Z', }, event: 'progress', - id: 'task-1:2026-07-20T01:02:03Z', + id: '2026-07-20T01:02:03Z:running:45', }, - { - data: { state: 'succeeded' }, + }) + const terminal = events.next() + await vi.advanceTimersByTimeAsync(5000) + await expect(terminal).resolves.toEqual({ + done: false, + value: { + data: { errorCode: undefined, state: 'succeeded' }, event: 'terminal', - id: 'task-1:terminal', + id: '2026-07-20T01:03:00Z:succeeded:100', }, - ]) - expect(mockStreamEvents).toHaveBeenCalledWith( + }) + await expect(events.next()).resolves.toEqual({ done: true, value: undefined }) + expect(listBackgroundTasks).toHaveBeenNthCalledWith( + 1, { - headers: { 'last-event-id': 'task-1:previous' }, - params: { - documentId: 'document/1', - id: 'space/1', - taskId: 'task/1', - }, + params: { control_space_id: 'space/1' }, + query: { limit: 200 }, }, - { + expect.objectContaining({ context: { silent: true }, - signal: abortController.signal, - }, - ) - }) - - it('rejects events without a resumable event id', async () => { - mockStreamEvents.mockResolvedValue( - eventIterator({ - data: { state: 'failed' }, - event: 'terminal', + signal: expect.any(AbortSignal), }), ) + }) - await expect(async () => { - for await (const event of streamProcessingTaskEvents({ - documentId: 'document-1', - knowledgeSpaceId: 'space-1', - taskId: 'task-1', - })) { - void event - } - }).rejects.toThrow('missing an event id') + it('continues through cursor pages and stops when the requested task is absent', async () => { + listBackgroundTasks + .mockResolvedValueOnce({ data: [], next_cursor: 'next-page' }) + .mockResolvedValueOnce({ data: [], next_cursor: null }) + + const events = streamProcessingTaskEvents({ + documentId: 'document-1', + knowledgeSpaceId: 'space-1', + taskId: 'task-1', + }) + + await expect(events.next()).resolves.toEqual({ done: true, value: undefined }) + expect(listBackgroundTasks).toHaveBeenNthCalledWith( + 2, + { + params: { control_space_id: 'space-1' }, + query: { cursor: 'next-page', limit: 200 }, + }, + expect.objectContaining({ + context: { silent: true }, + signal: expect.any(AbortSignal), + }), + ) + }) + + it('shares one paginated snapshot across concurrent task observers', async () => { + listBackgroundTasks + .mockResolvedValueOnce({ + data: [task('running', { documentId: 'document/2', id: 'task/2' })], + next_cursor: 'next-page', + }) + .mockResolvedValueOnce({ + data: [task('running', { documentId: 'document/1', id: 'task/1' })], + next_cursor: null, + }) + const firstController = new AbortController() + const secondController = new AbortController() + const firstEvents = streamProcessingTaskEvents({ + documentId: 'document/1', + knowledgeSpaceId: 'space/1', + signal: firstController.signal, + taskId: 'task/1', + }) + const secondEvents = streamProcessingTaskEvents({ + documentId: 'document/2', + knowledgeSpaceId: 'space/1', + signal: secondController.signal, + taskId: 'task/2', + }) + + const [first, second] = await Promise.all([firstEvents.next(), secondEvents.next()]) + + expect(first.done).toBe(false) + expect(second.done).toBe(false) + expect(listBackgroundTasks).toHaveBeenCalledTimes(2) + + firstController.abort() + secondController.abort() + await firstEvents.return(undefined) + await secondEvents.return(undefined) }) }) diff --git a/web/features/new-rag/__tests__/sources-page.spec.tsx b/web/features/new-rag/__tests__/sources-page.spec.tsx index de137089814..9bae36cc627 100644 --- a/web/features/new-rag/__tests__/sources-page.spec.tsx +++ b/web/features/new-rag/__tests__/sources-page.spec.tsx @@ -1,4 +1,4 @@ -import type { Source } from '@dify/contracts/knowledge-fs/types.gen' +import type { Source } from '../source-models' import { screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import datasetTranslations from '@/i18n/en-US/dataset.json' @@ -10,6 +10,21 @@ const toastErrorMock = vi.hoisted(() => vi.fn()) const permissionState = vi.hoisted(() => ({ workspacePermissionKeys: ['dataset.acl.edit', 'dataset.external.connect'], })) +const sourceApiResponse = vi.hoisted(() => (source: Source) => ({ + connection_id: source.connectionId ?? null, + created_at: source.createdAt, + credential_configured: source.credentialConfigured ?? null, + id: source.id, + knowledge_space_id: source.knowledgeSpaceId, + metadata: source.metadata, + name: source.name, + permission_scope: source.permissionScope ?? [], + status: source.status, + type: source.type, + updated_at: source.updatedAt, + uri: source.uri, + version: source.version ?? null, +})) vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: toastErrorMock, info: toastInfoMock }, @@ -22,12 +37,12 @@ vi.mock('@/context/permission-state', async () => { }) type SourcesInfiniteOptions = { - getNextPageParam: (lastPage: { nextCursor?: string }) => string | undefined + getNextPageParam: (lastPage: { next_cursor?: string | null }) => string | null | undefined input: (pageParam: string | null) => unknown initialPageParam: string | null refetchInterval: (query: { state: { - data?: { pages: Array<{ items: Source[] }> } + data?: { pages: Array<{ data: ReturnType[] }> } } }) => false | number } @@ -55,7 +70,17 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { const original = await importOriginal() return { ...original, - useInfiniteQuery: () => sourcesQuery, + useInfiniteQuery: () => ({ + ...sourcesQuery, + data: sourcesQuery.data + ? { + pages: sourcesQuery.data.pages.map((page) => ({ + data: page.items.map(sourceApiResponse), + next_cursor: page.nextCursor ?? null, + })), + } + : undefined, + }), useQueryClient: () => ({ invalidateQueries: invalidateQueriesMock }), } }) @@ -63,16 +88,35 @@ vi.mock('@tanstack/react-query', async (importOriginal) => { vi.mock('@/service/client', () => ({ consoleClient: { knowledgeFs: { - deleteKnowledgeSpacesByIdSourcesBySourceId: clientMock.deleteSource, - patchKnowledgeSpacesByIdSourcesBySourceId: clientMock.patchSource, - postKnowledgeSpacesByIdSourcesBySourceIdSync: clientMock.syncSource, + spaces: { + byControlSpaceId: { + sources: { + bySourceId: { + delete: clientMock.deleteSource, + patch: async (input: unknown) => + sourceApiResponse(await clientMock.patchSource(input)), + sync: { post: clientMock.syncSource }, + }, + get: { + infiniteOptions: infiniteOptionsMock, + key: vi.fn(() => ['sources']), + }, + }, + }, + }, }, }, consoleQuery: { knowledgeFs: { - getKnowledgeSpacesByIdSources: { - infiniteOptions: infiniteOptionsMock, - key: vi.fn(() => ['sources']), + spaces: { + byControlSpaceId: { + sources: { + get: { + infiniteOptions: infiniteOptionsMock, + key: vi.fn(() => ['sources']), + }, + }, + }, }, }, }, @@ -116,23 +160,27 @@ describe('SourcesPage', () => { expect(options).toBeDefined() if (!options) throw new Error('Expected source infinite query options') expect(options.input(null)).toEqual({ - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, query: { limit: 50 }, }) expect(options.input('next')).toEqual({ - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, query: { cursor: 'next', limit: 50 }, }) - expect(options.getNextPageParam({ nextCursor: 'next' })).toBe('next') + expect(options.getNextPageParam({ next_cursor: 'next' })).toBe('next') expect(options.initialPageParam).toBeNull() expect( options.refetchInterval({ - state: { data: { pages: [{ items: [source({ status: 'syncing' })] }] } }, + state: { + data: { pages: [{ data: [sourceApiResponse(source({ status: 'syncing' }))] }] }, + }, }), ).toBe(3000) expect( options.refetchInterval({ - state: { data: { pages: [{ items: [source({ status: 'active' })] }] } }, + state: { + data: { pages: [{ data: [sourceApiResponse(source({ status: 'active' }))] }] }, + }, }), ).toBe(false) expect(screen.getByRole('status')).toBeInTheDocument() @@ -425,6 +473,13 @@ describe('SourcesPage', () => { it('syncs a source through the real KnowledgeFS action', async () => { const user = userEvent.setup() sourcesQuery.data = { pages: [{ items: [source({})] }] } + let finishRefresh: (() => void) | undefined + invalidateQueriesMock.mockImplementationOnce( + () => + new Promise((resolve) => { + finishRefresh = resolve + }), + ) render() await user.click( @@ -438,7 +493,7 @@ describe('SourcesPage', () => { await waitFor(() => expect(clientMock.syncSource).toHaveBeenCalledWith({ headers: { 'Idempotency-Key': expect.any(String) }, - params: { id: 'space-1', sourceId: 'source-1' }, + params: { control_space_id: 'space-1', source_id: 'source-1' }, }), ) expect( @@ -446,14 +501,24 @@ describe('SourcesPage', () => { 'dataset.newKnowledge.sourceStatus.syncing', ), ).toBeInTheDocument() + finishRefresh?.() + await waitFor(() => + expect( + within(screen.getByRole('row', { name: /Product documentation/ })).getByText( + 'dataset.newKnowledge.sourceStatus.active', + ), + ).toBeInTheDocument(), + ) const options = infiniteOptionsMock.mock.lastCall?.[0] expect(options).toBeDefined() if (!options) throw new Error('Expected source infinite query options') expect( options.refetchInterval({ - state: { data: { pages: [{ items: [source({ status: 'active' })] }] } }, + state: { + data: { pages: [{ data: [sourceApiResponse(source({ status: 'active' }))] }] }, + }, }), - ).toBe(3000) + ).toBe(false) expect(invalidateQueriesMock).toHaveBeenCalledWith({ queryKey: ['sources'] }) }) @@ -482,7 +547,7 @@ describe('SourcesPage', () => { await waitFor(() => expect(clientMock.patchSource).toHaveBeenCalledWith({ body: { expectedVersion: 3, status: 'disabled' }, - params: { id: 'space-1', sourceId: 'active-source' }, + params: { control_space_id: 'space-1', source_id: 'active-source' }, }), ) @@ -491,7 +556,7 @@ describe('SourcesPage', () => { await waitFor(() => expect(clientMock.patchSource).toHaveBeenLastCalledWith({ body: { expectedVersion: 3, status: 'active' }, - params: { id: 'space-1', sourceId: 'disabled' }, + params: { control_space_id: 'space-1', source_id: 'disabled' }, }), ) }) @@ -538,7 +603,7 @@ describe('SourcesPage', () => { await waitFor(() => expect(clientMock.patchSource).toHaveBeenLastCalledWith({ body: { expectedVersion: 4, status: 'active' }, - params: { id: 'space-1', sourceId: 'source-1' }, + params: { control_space_id: 'space-1', source_id: 'source-1' }, }), ) }) @@ -560,8 +625,8 @@ describe('SourcesPage', () => { await waitFor(() => expect(clientMock.deleteSource).toHaveBeenCalledWith({ body: { expectedRevision: 3 }, - headers: { 'idempotency-key': expect.any(String) }, - params: { id: 'space-1', sourceId: 'source-1' }, + headers: { 'Idempotency-Key': expect.any(String) }, + params: { control_space_id: 'space-1', source_id: 'source-1' }, query: { documents: 'keep' }, }), ) @@ -594,6 +659,13 @@ describe('SourcesPage', () => { it('retries an errored source and shows its queued state', async () => { const user = userEvent.setup() sourcesQuery.data = { pages: [{ items: [source({ status: 'error' })] }] } + let finishRefresh: (() => void) | undefined + invalidateQueriesMock.mockImplementationOnce( + () => + new Promise((resolve) => { + finishRefresh = resolve + }), + ) render() await user.click(screen.getByRole('button', { name: 'common.operation.retry' })) @@ -604,6 +676,7 @@ describe('SourcesPage', () => { 'dataset.newKnowledge.sourceStatus.syncing', ), ).toBeInTheDocument() + finishRefresh?.() }) it('supports row selection and a true indeterminate select-all state', async () => { diff --git a/web/features/new-rag/__tests__/website-crawl-preview.spec.tsx b/web/features/new-rag/__tests__/website-crawl-preview.spec.tsx index f8a3ecf6fde..ab69a280da3 100644 --- a/web/features/new-rag/__tests__/website-crawl-preview.spec.tsx +++ b/web/features/new-rag/__tests__/website-crawl-preview.spec.tsx @@ -1,4 +1,4 @@ -import type { Source, SourceWorkflowRun } from '@dify/contracts/knowledge-fs/types.gen' +import type { Source, SourceWorkflowRun } from '../source-models' import { act, screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { render } from '@/test/console/render' @@ -13,6 +13,63 @@ const clientMock = vi.hoisted(() => ({ retry: vi.fn(), startPreview: vi.fn(), })) +const sourceApiResponse = vi.hoisted(() => (source: Source) => ({ + connection_id: source.connectionId ?? null, + created_at: source.createdAt, + credential_configured: source.credentialConfigured ?? null, + id: source.id, + knowledge_space_id: source.knowledgeSpaceId, + metadata: source.metadata, + name: source.name, + permission_scope: source.permissionScope ?? [], + status: source.status, + type: source.type, + updated_at: source.updatedAt, + uri: source.uri, + version: source.version ?? null, +})) +const workflowApiResponse = vi.hoisted(() => (workflow: SourceWorkflowRun) => ({ + canceled_at: workflow.canceledAt ?? null, + checkpoint: workflow.checkpoint, + completed_at: workflow.completedAt ?? null, + created_at: workflow.createdAt, + cursor: workflow.cursor ?? null, + execution_attempts: workflow.executionAttempts, + id: workflow.id, + knowledge_space_id: workflow.knowledgeSpaceId, + kind: workflow.kind, + last_error_code: workflow.lastErrorCode ?? null, + max_execution_attempts: workflow.maxExecutionAttempts, + progress_completed: workflow.progressCompleted, + progress_failed: workflow.progressFailed, + progress_skipped: workflow.progressSkipped, + progress_total: workflow.progressTotal ?? null, + source_id: workflow.sourceId ?? null, + state: workflow.state, + updated_at: workflow.updatedAt, +})) +const crawlPreviewPageListApiResponse = vi.hoisted( + () => + (response: { + items: Array<{ + description?: string + etag?: string + pageId: string + sourceUrl: string + title?: string + }> + nextCursor?: string + }) => ({ + data: response.items.map((page) => ({ + description: page.description ?? null, + etag: page.etag ?? null, + page_id: page.pageId, + source_url: page.sourceUrl, + title: page.title ?? null, + })), + next_cursor: response.nextCursor ?? null, + }), +) const routerMock = vi.hoisted(() => ({ push: vi.fn() })) @@ -51,13 +108,41 @@ vi.mock('../crawl-selection-form', () => ({ vi.mock('@/service/client', () => ({ consoleClient: { knowledgeFs: { - getKnowledgeSpacesByIdSources: clientMock.listSources, - getKnowledgeSpacesByIdSourceWorkflowsByRunId: clientMock.getRun, - getKnowledgeSpacesByIdSourceWorkflowsByRunIdPages: clientMock.getPages, - postKnowledgeSpacesByIdSources: clientMock.createSource, - postKnowledgeSpacesByIdSourcesBySourceIdCrawlPreview: clientMock.startPreview, - postKnowledgeSpacesByIdSourceWorkflowsByRunIdCancel: clientMock.cancel, - postKnowledgeSpacesByIdSourceWorkflowsByRunIdRetry: clientMock.retry, + spaces: { + byControlSpaceId: { + sourceWorkflows: { + byRunId: { + cancel: { + post: async (input: unknown) => workflowApiResponse(await clientMock.cancel(input)), + }, + get: async (input: unknown) => workflowApiResponse(await clientMock.getRun(input)), + pages: { + get: async (input: unknown) => + crawlPreviewPageListApiResponse(await clientMock.getPages(input)), + }, + retry: { + post: async (input: unknown) => workflowApiResponse(await clientMock.retry(input)), + }, + }, + }, + sources: { + bySourceId: { + crawlPreview: { + post: async (input: unknown) => + workflowApiResponse(await clientMock.startPreview(input)), + }, + }, + get: async (input: unknown) => { + const response = await clientMock.listSources(input) + return { + data: response.items.map(sourceApiResponse), + next_cursor: response.nextCursor ?? null, + } + }, + post: async (input: unknown) => sourceApiResponse(await clientMock.createSource(input)), + }, + }, + }, }, }, })) @@ -116,6 +201,7 @@ describe('WebsiteCrawlPreview', () => { beforeEach(() => { vi.useRealTimers() for (const mock of Object.values(clientMock)) mock.mockReset() + clientMock.cancel.mockResolvedValue(run('canceled')) clientMock.createSource.mockResolvedValue(source()) clientMock.startPreview.mockResolvedValue(run('running')) clientMock.getRun.mockResolvedValue( @@ -181,11 +267,11 @@ describe('WebsiteCrawlPreview', () => { type: 'web', uri: 'https://docs.dify.ai/', }, - params: { id: 'space-1' }, + params: { control_space_id: 'space-1' }, }) expect(clientMock.startPreview).toHaveBeenCalledWith({ headers: { 'Idempotency-Key': expect.any(String) }, - params: { id: 'space-1', sourceId: 'source-1' }, + params: { control_space_id: 'space-1', source_id: 'source-1' }, }) expect(await screen.findByText('Getting started')).toBeInTheDocument() expect(screen.getByText(/^dataset\.newKnowledge\.pagesCrawled/)).toHaveAttribute( @@ -198,6 +284,31 @@ describe('WebsiteCrawlPreview', () => { ).not.toBeInTheDocument() }) + it('cancels a preview-ready workflow and starts a fresh run when re-crawling', async () => { + clientMock.cancel.mockResolvedValue(run('canceled')) + + render() + const user = await fillValidForm() + await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.crawlAndPreview' })) + await screen.findByText('Getting started') + const firstIdempotencyKey = + clientMock.startPreview.mock.calls[0]?.[0].headers['Idempotency-Key'] + + await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.reCrawl' })) + + await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledOnce()) + await waitFor(() => expect(clientMock.startPreview).toHaveBeenCalledTimes(2)) + expect(clientMock.cancel).toHaveBeenCalledWith({ + body: { reason: 'user_requested' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, + }) + expect(clientMock.retry).not.toHaveBeenCalled() + expect(clientMock.createSource).toHaveBeenCalledOnce() + expect(clientMock.startPreview.mock.calls[1]?.[0].headers['Idempotency-Key']).not.toBe( + firstIdempotencyKey, + ) + }) + it('submits the crawl form with Enter and enforces the source name contract limit', async () => { render() const user = await fillValidForm() @@ -234,6 +345,12 @@ describe('WebsiteCrawlPreview', () => { await user.clear(pageLimit) await user.type(pageLimit, '50') expect(pageLimit).toHaveValue(50) + await user.click(screen.getByRole('button', { name: /^dataset\.newKnowledge\.crawlOptions/ })) + expect( + screen.getByText( + 'dataset.newKnowledge.includeSubpages: dataset.newKnowledge.booleanTrue · dataset.newKnowledge.maxPages: 50', + ), + ).toBeInTheDocument() await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.crawlAndPreview' })) await waitFor(() => expect(clientMock.createSource).toHaveBeenCalledOnce()) @@ -285,7 +402,7 @@ describe('WebsiteCrawlPreview', () => { await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledOnce()) expect(clientMock.cancel).toHaveBeenCalledWith({ body: { reason: 'user_requested' }, - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, }) await waitFor(() => expect(routerMock.push).toHaveBeenCalledWith('/datasets/new/space-1/sources'), @@ -364,10 +481,14 @@ describe('WebsiteCrawlPreview', () => { expect(clientMock.cancel).not.toHaveBeenCalled() }) - it('cancels a retry that returns after navigation discard was confirmed', async () => { - const retryRequest = deferred() - clientMock.retry.mockReturnValue(retryRequest.promise) - clientMock.cancel.mockResolvedValue(run('canceled')) + it('cancels a fresh re-crawl that returns after navigation discard was confirmed', async () => { + const recrawlRequest = deferred() + clientMock.startPreview + .mockResolvedValueOnce(run('running')) + .mockReturnValueOnce(recrawlRequest.promise) + clientMock.cancel + .mockResolvedValueOnce(run('canceled')) + .mockResolvedValueOnce(run('canceled', { id: 'run-2' })) render( <> Documents navigation @@ -378,28 +499,29 @@ describe('WebsiteCrawlPreview', () => { await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.crawlAndPreview' })) await screen.findByText('Getting started') await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.reCrawl' })) - await waitFor(() => expect(clientMock.retry).toHaveBeenCalledOnce()) + await waitFor(() => expect(clientMock.startPreview).toHaveBeenCalledTimes(2)) await user.click(screen.getByRole('link', { name: 'Documents navigation' })) await user.click( screen.getByRole('button', { name: 'dataset.newKnowledge.discardSourceChangesConfirm' }), ) - retryRequest.resolve(run('running')) + recrawlRequest.resolve(run('running', { id: 'run-2' })) - await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledOnce()) + await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledTimes(2)) + expect(clientMock.retry).not.toHaveBeenCalled() await waitFor(() => expect(routerMock.push).toHaveBeenCalledWith('/datasets/new/space-1/documents'), ) }) - it('reconciles a response-lost retry before leaving the preview', async () => { - const previousRun = run('succeeded', { progressCompleted: 1, progressTotal: 1 }) - clientMock.getRun - .mockResolvedValueOnce(previousRun) - .mockResolvedValueOnce(previousRun) - .mockResolvedValueOnce(run('running', { executionAttempts: 2 })) - clientMock.retry.mockRejectedValue(new Error('response lost')) - clientMock.cancel.mockResolvedValue(run('canceled', { executionAttempts: 2 })) + it('reconciles a response-lost fresh re-crawl before leaving the preview', async () => { + clientMock.startPreview + .mockResolvedValueOnce(run('running')) + .mockRejectedValueOnce(new Error('response lost')) + .mockResolvedValueOnce(run('running', { id: 'run-2' })) + clientMock.cancel + .mockResolvedValueOnce(run('canceled')) + .mockResolvedValueOnce(run('canceled', { id: 'run-2' })) render( <> Documents navigation @@ -410,15 +532,18 @@ describe('WebsiteCrawlPreview', () => { await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.crawlAndPreview' })) await screen.findByText('Getting started') await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.reCrawl' })) - await waitFor(() => expect(clientMock.getRun).toHaveBeenCalledTimes(2)) + await waitFor(() => expect(clientMock.startPreview).toHaveBeenCalledTimes(2)) await user.click(screen.getByRole('link', { name: 'Documents navigation' })) await user.click( screen.getByRole('button', { name: 'dataset.newKnowledge.discardSourceChangesConfirm' }), ) - await waitFor(() => expect(clientMock.getRun).toHaveBeenCalledTimes(3)) - await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledOnce()) + await waitFor(() => expect(clientMock.startPreview).toHaveBeenCalledTimes(3)) + await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledTimes(2)) + expect(clientMock.startPreview.mock.calls[1]?.[0].headers).toEqual( + clientMock.startPreview.mock.calls[2]?.[0].headers, + ) await waitFor(() => expect(routerMock.push).toHaveBeenCalledWith('/datasets/new/space-1/documents'), ) @@ -448,37 +573,17 @@ describe('WebsiteCrawlPreview', () => { ) await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledTimes(2)) - expect(clientMock.cancel.mock.calls[0]?.[0].params.runId).toBe('run-1') - expect(clientMock.cancel.mock.calls[1]?.[0].params.runId).toBe('run-1') + expect(clientMock.cancel.mock.calls[0]?.[0].params.run_id).toBe('run-1') + expect(clientMock.cancel.mock.calls[1]?.[0].params.run_id).toBe('run-1') await waitFor(() => expect(routerMock.push).toHaveBeenCalledWith('/datasets/new/space-1/sources'), ) }) - it('restores polling after cancel failure and honors the latest terminal snapshot', async () => { - const retryRequest = deferred() - clientMock.getRun - .mockResolvedValueOnce(run('succeeded', { progressCompleted: 1, progressTotal: 1 })) - .mockResolvedValueOnce( - run('canceled', { - executionAttempts: 2, - progressCompleted: 1, - updatedAt: '2026-07-20T10:02:00Z', - }), - ) - clientMock.getPages - .mockResolvedValueOnce({ - items: [ - { - pageId: 'page-1', - sourceUrl: 'https://docs.dify.ai/getting-started', - title: 'Getting started', - }, - ], - }) - .mockResolvedValueOnce({ items: [] }) - clientMock.retry.mockReturnValue(retryRequest.promise) - clientMock.cancel.mockRejectedValue(Object.assign(new Error('conflict'), { status: 409 })) + it('keeps the preview available when re-crawl cancellation fails', async () => { + clientMock.cancel + .mockRejectedValueOnce(Object.assign(new Error('conflict'), { status: 409 })) + .mockResolvedValueOnce(run('canceled')) render( <> Documents navigation @@ -489,28 +594,16 @@ describe('WebsiteCrawlPreview', () => { await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.crawlAndPreview' })) await screen.findByText('Getting started') await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.reCrawl' })) - await user.click(screen.getByRole('link', { name: 'Documents navigation' })) - await user.click( - screen.getByRole('button', { name: 'dataset.newKnowledge.discardSourceChangesConfirm' }), - ) - retryRequest.resolve( - run('running', { executionAttempts: 2, updatedAt: '2026-07-20T10:01:00Z' }), - ) - - expect(await within(screen.getByRole('alertdialog')).findByRole('alert')).toHaveTextContent( - 'dataset.newKnowledge.crawlFailedDescription', - ) - await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.keepEditing' })) - await waitFor(() => expect(clientMock.getRun).toHaveBeenCalledTimes(2)) - expect(await screen.findByText('dataset.newKnowledge.crawlStopped')).toBeInTheDocument() - expect(screen.queryByText('Getting started')).not.toBeInTheDocument() + await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledOnce()) + expect(clientMock.startPreview).toHaveBeenCalledOnce() + expect(screen.getByText('Getting started')).toBeInTheDocument() await user.click(screen.getByRole('link', { name: 'Documents navigation' })) await user.click( screen.getByRole('button', { name: 'dataset.newKnowledge.discardSourceChangesConfirm' }), ) - expect(clientMock.cancel).toHaveBeenCalledOnce() + await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledTimes(2)) await waitFor(() => expect(routerMock.push).toHaveBeenCalledWith('/datasets/new/space-1/documents'), ) @@ -622,9 +715,9 @@ describe('WebsiteCrawlPreview', () => { expect(routerMock.push).not.toHaveBeenCalledWith('/datasets/new/space-1/documents') }) - it('shows pending feedback while a completed crawl is being restarted', async () => { - const retryRequest = deferred() - clientMock.retry.mockReturnValue(retryRequest.promise) + it('shows pending feedback while a preview-ready crawl is being restarted', async () => { + const cancelRequest = deferred() + clientMock.cancel.mockReturnValue(cancelRequest.promise) render() const user = await fillValidForm() @@ -633,12 +726,9 @@ describe('WebsiteCrawlPreview', () => { await user.click(reCrawl) expect(reCrawl).toHaveAttribute('aria-disabled', 'true') - await act(async () => - retryRequest.resolve( - run('running', { executionAttempts: 2, updatedAt: '2026-07-20T10:01:00Z' }), - ), - ) - await waitFor(() => expect(clientMock.retry).toHaveBeenCalledOnce()) + await act(async () => cancelRequest.resolve(run('canceled'))) + await waitFor(() => expect(clientMock.startPreview).toHaveBeenCalledTimes(2)) + expect(clientMock.retry).not.toHaveBeenCalled() }) it('streams page cursors while running and replaces them with the final snapshot', async () => { @@ -681,14 +771,14 @@ describe('WebsiteCrawlPreview', () => { expect(await screen.findByText('Two')).toBeInTheDocument() expect(clientMock.getRun).toHaveBeenNthCalledWith(1, { - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, }) expect(clientMock.getPages).toHaveBeenNthCalledWith(1, { - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, query: { limit: 200 }, }) expect(clientMock.getPages).toHaveBeenNthCalledWith(2, { - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, query: { cursor: 'page-2', limit: 200 }, }) expect( @@ -709,11 +799,11 @@ describe('WebsiteCrawlPreview', () => { expect(screen.queryByText('Old one')).not.toBeInTheDocument() expect(screen.queryByText('Deleted page')).not.toBeInTheDocument() expect(clientMock.getPages).toHaveBeenNthCalledWith(3, { - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, query: { limit: 200 }, }) expect(clientMock.getPages).toHaveBeenNthCalledWith(4, { - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, query: { cursor: 'final-page-2', limit: 200 }, }) expect( @@ -738,7 +828,7 @@ describe('WebsiteCrawlPreview', () => { await waitFor(() => expect(clientMock.cancel).toHaveBeenCalledOnce()) expect(clientMock.cancel).toHaveBeenCalledWith({ body: { reason: 'user_requested' }, - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, }) expect(screen.getByText('Getting started')).toBeInTheDocument() expect(await screen.findByText('dataset.newKnowledge.crawlStopped')).toHaveAttribute( @@ -822,7 +912,7 @@ describe('WebsiteCrawlPreview', () => { await waitFor(() => expect(clientMock.retry).toHaveBeenCalledOnce()) expect(clientMock.retry).toHaveBeenCalledWith({ - params: { id: 'space-1', runId: 'run-1' }, + params: { control_space_id: 'space-1', run_id: 'run-1' }, }) expect(clientMock.createSource).toHaveBeenCalledOnce() expect(clientMock.startPreview).toHaveBeenCalledOnce() @@ -861,7 +951,7 @@ describe('WebsiteCrawlPreview', () => { .mockResolvedValueOnce(run('running')) .mockResolvedValueOnce(failedRun) .mockResolvedValueOnce( - run('succeeded', { progressCompleted: 1, updatedAt: '2026-07-20T10:01:00Z' }), + run('preview_ready', { progressCompleted: 1, updatedAt: '2026-07-20T10:01:00Z' }), ) clientMock.getPages.mockResolvedValueOnce({ items: [] }).mockResolvedValue({ items: [ @@ -873,6 +963,7 @@ describe('WebsiteCrawlPreview', () => { ], }) clientMock.retry.mockResolvedValue(run('running')) + clientMock.cancel.mockResolvedValue(run('canceled')) render() const user = await fillValidForm() @@ -892,7 +983,9 @@ describe('WebsiteCrawlPreview', () => { await screen.findByText('Getting started') await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.reCrawl' })) - await waitFor(() => expect(clientMock.retry).toHaveBeenCalledTimes(2)) + await waitFor(() => expect(clientMock.startPreview).toHaveBeenCalledTimes(2)) + expect(clientMock.retry).toHaveBeenCalledOnce() + expect(clientMock.cancel).toHaveBeenCalledOnce() }) it('reconciles a lost Retry response without sending retry twice', async () => { @@ -987,9 +1080,8 @@ describe('WebsiteCrawlPreview', () => { }) it('offers an adjust-and-recrawl path after a successful zero-result crawl', async () => { - clientMock.getRun.mockResolvedValue(run('succeeded')) + clientMock.getRun.mockResolvedValue(run('zero_results')) clientMock.getPages.mockResolvedValue({ items: [] }) - clientMock.retry.mockResolvedValue(run('running')) render() const user = await fillValidForm() @@ -998,7 +1090,9 @@ describe('WebsiteCrawlPreview', () => { const noPages = await screen.findByText(/^dataset\.newKnowledge\.noPagesFound:/) expect(noPages.closest('[role="status"]')).toHaveAttribute('aria-live', 'polite') await user.click(screen.getByRole('button', { name: 'dataset.newKnowledge.adjustAndRecrawl' })) - await waitFor(() => expect(clientMock.retry).toHaveBeenCalledOnce()) + await waitFor(() => expect(clientMock.startPreview).toHaveBeenCalledTimes(2)) + expect(clientMock.retry).not.toHaveBeenCalled() + expect(clientMock.cancel).not.toHaveBeenCalled() }) it('treats a superseded workflow as terminal and stops polling', async () => { diff --git a/web/features/new-rag/add-source-page.tsx b/web/features/new-rag/add-source-page.tsx index ecf5721be3c..6d0062d2e29 100644 --- a/web/features/new-rag/add-source-page.tsx +++ b/web/features/new-rag/add-source-page.tsx @@ -1,20 +1,19 @@ 'use client' -import type { - GetKnowledgeSpacesByIdSourceConnectionsResponse, - GetSourceProvidersResponse, -} from '@dify/contracts/knowledge-fs/types.gen' +import type { DatasourceProviderAuthListResponse } from '@dify/contracts/api/console/auth/types.gen' import type { NewKnowledgeSourceDraft, NewKnowledgeSourceType, NewKnowledgeWebsiteProvider, } from './routes' +import type { SourceConnection as Connection, SourceProvider as Provider } from './source-models' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { useInfiniteQuery, useQuery, useQueryClient } from '@tanstack/react-query' import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' +import { buildIntegrationPath } from '@/app/components/integrations/routes' import { useRouter } from '@/next/navigation' import { consoleClient, consoleQuery } from '@/service/client' import { PendingWebsiteSetup, UnavailableConnectedSourceSetup } from './add-source-placeholder' @@ -25,11 +24,14 @@ import { newKnowledgeSourceDraftStorageKey, parseNewKnowledgeSourceDraft, } from './routes' +import { + sourceConnectionFromApi, + sourceConnectionListFromApi, + sourceProviderListFromApi, +} from './source-models' import { WebsiteCrawlPreview } from './website-crawl-preview' -type Provider = GetSourceProvidersResponse['items'][number] type ProviderField = Provider['configuration'][number] -type Connection = GetKnowledgeSpacesByIdSourceConnectionsResponse['items'][number] type ConnectionAuthKind = 'api-key' | 'endpoint' type SourceType = NewKnowledgeSourceType @@ -40,6 +42,7 @@ const FIRECRAWL_CONFIGURATION = { datasource: 'crawl', pluginId: 'langgenius/firecrawl_datasource', provider: 'firecrawl', + providerKind: 'website', } as const const WEBSITE_PROVIDER_OPTIONS: Array<{ icon: string @@ -49,7 +52,10 @@ const WEBSITE_PROVIDER_OPTIONS: Array<{ { icon: 'i-custom-public-llm-jina', value: 'Jina Reader' }, { icon: 'i-ri-water-flash-line', value: 'WaterCrawl' }, ] -const FIRECRAWL_FIXED_FIELD_NAMES = new Set(Object.keys(FIRECRAWL_CONFIGURATION)) +const FIRECRAWL_FIXED_FIELD_NAMES = new Set([ + ...Object.keys(FIRECRAWL_CONFIGURATION), + 'credentialId', +]) const CONNECTION_STATUS_PRIORITY: Record = { active: 0, provisioning: 1, @@ -74,6 +80,18 @@ function findFirecrawl(providers: Provider[]) { return providers.find((provider) => provider.id === FIRECRAWL_PROVIDER_ID) } +function findFirecrawlCredential(providers: DatasourceProviderAuthListResponse['result']) { + const datasourceProvider = providers.find( + (provider) => + provider.plugin_id === FIRECRAWL_CONFIGURATION.pluginId && + provider.provider === FIRECRAWL_CONFIGURATION.provider, + ) + return ( + datasourceProvider?.credentials_list.find((credential) => credential.is_default) ?? + datasourceProvider?.credentials_list[0] + ) +} + function findProviderConnection(connections: Connection[], providerId?: string) { if (!providerId) return undefined return [ @@ -101,7 +119,17 @@ function normalizeSourceType(value: string | null): SourceType { return 'websiteCrawl' } -function getSupportedAuthKinds(provider: Provider) { +function isDifyManagedProvider(provider: Provider) { + const fieldNames = new Set(provider.configuration.map((field) => field.name)) + return fieldNames.has('credentialId') && fieldNames.has('providerKind') +} + +function getSupportedAuthKinds(provider: Provider, credentialId?: string) { + if (isDifyManagedProvider(provider)) + return credentialId && provider.authKinds.includes('endpoint') + ? (['endpoint'] satisfies ConnectionAuthKind[]) + : [] + const fields = provider.configuration.filter( (field) => !FIRECRAWL_FIXED_FIELD_NAMES.has(field.name), ) @@ -275,15 +303,17 @@ function ConnectionForm({ onDraftChange, onReconcile, provider, + credentialId, }: { knowledgeSpaceId: string onConnected: (connection: Connection) => void onDraftChange: (dirty: boolean) => void onReconcile: () => Promise provider: Provider + credentialId?: string }) { const { t } = useTranslation('dataset') - const supportedAuthKinds = getSupportedAuthKinds(provider) + const supportedAuthKinds = getSupportedAuthKinds(provider, credentialId) const [authKind, setAuthKind] = useState(supportedAuthKinds[0] ?? 'api-key') const [configuration, setConfiguration] = useState>({}) const [credentials, setCredentials] = useState>({}) @@ -323,13 +353,17 @@ function ConnectionForm({ setError(false) setPending(true) try { + const fixedValues: Record = { + ...FIRECRAWL_CONFIGURATION, + ...(credentialId ? { credentialId } : {}), + } const fixedConfiguration = Object.fromEntries( provider.configuration .filter((field) => FIRECRAWL_FIXED_FIELD_NAMES.has(field.name)) - .map((field) => [ - field.name, - FIRECRAWL_CONFIGURATION[field.name as keyof typeof FIRECRAWL_CONFIGURATION], - ]), + .flatMap((field) => { + const value = fixedValues[field.name] + return value === undefined ? [] : ([[field.name, value]] as const) + }), ) const safeConfiguration = { ...fixedConfiguration, @@ -344,8 +378,8 @@ function ConnectionForm({ .filter((field) => field.secret && credentials[field.name]?.trim()) .map((field) => [field.name, fieldValue(credentials[field.name] ?? '', field.type)]), ) - const createdConnection = - await consoleClient.knowledgeFs.postKnowledgeSpacesByIdSourceConnections({ + const createdConnection = sourceConnectionFromApi( + await consoleClient.knowledgeFs.spaces.byControlSpaceId.sourceConnections.post({ body: { authKind, configuration: safeConfiguration, @@ -353,8 +387,9 @@ function ConnectionForm({ name: FIRECRAWL_CONNECTION_NAME, providerId: provider.id, }, - params: { id: knowledgeSpaceId }, - }) + params: { control_space_id: knowledgeSpaceId }, + }), + ) setCredentials({}) onDraftChange(false) onConnected(createdConnection) @@ -416,7 +451,9 @@ function ConnectionForm({ ) @@ -425,22 +462,28 @@ function ConnectionForm({ function UnconfiguredProvider({ knowledgeSpaceId, onConnected, + onConfigureManagedProvider, onDraftChange, onReconcile, provider, + credentialId, }: { knowledgeSpaceId: string onConnected: (connection: Connection) => void + onConfigureManagedProvider: () => void onDraftChange: (dirty: boolean) => void onReconcile: () => Promise provider: Provider + credentialId?: string }) { const { t } = useTranslation('dataset') const [configuring, setConfiguring] = useState(false) + const difyManaged = isDifyManagedProvider(provider) - if (configuring) + if ((difyManaged && credentialId) || configuring) return (

- {t(($) => $['newKnowledge.providerNotConfiguredDescription'], { - provider: FIRECRAWL_CONNECTION_NAME, - })} + {difyManaged + ? t(($) => $['newKnowledge.providerCredentialRequiredDescription'], { + provider: FIRECRAWL_CONNECTION_NAME, + }) + : t(($) => $['newKnowledge.providerNotConfiguredDescription'], { + provider: FIRECRAWL_CONNECTION_NAME, + })}

- ) @@ -494,13 +547,17 @@ function ConnectionProblem({ setPending(true) setError(false) try { - const refreshed = - await consoleClient.knowledgeFs.postKnowledgeSpacesByIdSourceConnectionsByConnectionIdRefresh( + const refreshed = sourceConnectionFromApi( + await consoleClient.knowledgeFs.spaces.byControlSpaceId.sourceConnections.byConnectionId.refresh.post( { body: { expectedVersion: connection.version }, - params: { connectionId: connection.id, id: knowledgeSpaceId }, + params: { + connection_id: connection.id, + control_space_id: knowledgeSpaceId, + }, }, - ) + ), + ) onConnected(refreshed) } catch { let reconciledConnection: Connection | undefined @@ -657,31 +714,42 @@ export function AddSourcePage({ } }, [sourceDraftKey]) const providersQuery = useQuery( - consoleQuery.knowledgeFs.getSourceProviders.queryOptions({ - input: {}, + consoleQuery.knowledgeFs.spaces.byControlSpaceId.sourceProviders.get.queryOptions({ + input: { params: { control_space_id: knowledgeSpaceId } }, + context: { silent: true }, + enabled: websiteSourceSelected, + retry: false, + select: sourceProviderListFromApi, + }), + ) + const datasourceAuthQuery = useQuery( + consoleQuery.auth.plugin.datasource.defaultList.get.queryOptions({ context: { silent: true }, enabled: websiteSourceSelected, retry: false, }), ) const connectionsQuery = useInfiniteQuery( - consoleQuery.knowledgeFs.getKnowledgeSpacesByIdSourceConnections.infiniteOptions({ + consoleQuery.knowledgeFs.spaces.byControlSpaceId.sourceConnections.get.infiniteOptions({ context: { silent: true }, enabled: websiteSourceSelected, input: (pageParam) => ({ - params: { id: knowledgeSpaceId }, + params: { control_space_id: knowledgeSpaceId }, query: { limit: CONNECTION_PAGE_SIZE, ...(typeof pageParam === 'string' ? { cursor: pageParam } : {}), }, }), - getNextPageParam: (lastPage) => lastPage.nextCursor, + getNextPageParam: (lastPage) => lastPage.next_cursor, initialPageParam: null as string | null, retry: false, }), ) - const provider = findFirecrawl(providersQuery.data?.items ?? []) - const remoteConnections = connectionsQuery.data?.pages.flatMap((page) => page.items) ?? [] + const provider = findFirecrawl(providersQuery.data ?? []) + const datasourceCredential = findFirecrawlCredential(datasourceAuthQuery.data?.result ?? []) + const difyManagedProvider = provider ? isDifyManagedProvider(provider) : false + const remoteConnections = + connectionsQuery.data?.pages.flatMap((page) => sourceConnectionListFromApi(page).items) ?? [] const remoteConnection = findProviderConnection(remoteConnections, provider?.id) const [connectionOverride, setConnectionOverride] = useState() const matchingRemoteConnection = connectionOverride @@ -706,7 +774,11 @@ export function AddSourcePage({ } return localConnection }, [connectionOverride, matchingRemoteConnection, provider?.id, remoteConnection]) - const supportsDirectConnection = provider ? getSupportedAuthKinds(provider).length > 0 : false + const supportsDirectConnection = provider + ? difyManagedProvider + ? provider.authKinds.includes('endpoint') + : getSupportedAuthKinds(provider).length > 0 + : false const { fetchNextPage: fetchNextConnectionPage, hasNextPage: hasNextConnectionPage, @@ -734,7 +806,7 @@ export function AddSourcePage({ (updatedConnection: Connection) => { setConnectionOverride(updatedConnection) void queryClient.invalidateQueries({ - queryKey: consoleQuery.knowledgeFs.getKnowledgeSpacesByIdSourceConnections.key(), + queryKey: consoleQuery.knowledgeFs.spaces.byControlSpaceId.sourceConnections.get.key(), }) }, [queryClient], @@ -744,7 +816,8 @@ export function AddSourcePage({ if (connection) setConnectionOverride(connection) const refreshed = await refetchConnections() if (refreshed.error) throw refreshed.error - const refreshedConnections = refreshed.data?.pages.flatMap((page) => page.items) ?? [] + const refreshedConnections = + refreshed.data?.pages.flatMap((page) => sourceConnectionListFromApi(page).items) ?? [] const refreshedCurrentConnection = connection ? findConnectionById(refreshedConnections, connection.id) : undefined @@ -762,7 +835,10 @@ export function AddSourcePage({ (!connectionsQuery.isFetchNextPageError && (connectionsQuery.hasNextPage || connectionsQuery.isFetchingNextPage)) const queryError = - providersQuery.error || connectionsQuery.error || connectionsQuery.isFetchNextPageError + providersQuery.error || + connectionsQuery.error || + connectionsQuery.isFetchNextPageError || + (difyManagedProvider ? datasourceAuthQuery.error : null) const websiteReady = Boolean( websiteSourceSelected && !queryError && @@ -933,7 +1009,8 @@ export function AddSourcePage({ if ( !sourceDraftResolved || - (websiteSourceSelected && (providersQuery.isPending || loadingConnections)) + (websiteSourceSelected && + (providersQuery.isPending || datasourceAuthQuery.isPending || loadingConnections)) ) return (
@@ -987,7 +1064,11 @@ export function AddSourcePage({

- {knowledgeSpace.description || t(($) => $['newKnowledge.noDescription'])} + {summary?.description || t(($) => $['newKnowledge.noDescription'])}

$['newKnowledge.tags'])}. ${unavailable}`} diff --git a/web/features/new-rag/crawl-selection-form.tsx b/web/features/new-rag/crawl-selection-form.tsx index c46c4d73a3d..efd31d7c996 100644 --- a/web/features/new-rag/crawl-selection-form.tsx +++ b/web/features/new-rag/crawl-selection-form.tsx @@ -1,13 +1,13 @@ 'use client' +import type { FormEvent } from 'react' import type { - GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse, - GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponse, - PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyData, + CrawlPreviewPage as PreviewPage, Source, SourceWorkflowRun, -} from '@dify/contracts/knowledge-fs/types.gen' -import type { FormEvent } from 'react' + SourceSyncPolicy as SyncPolicy, + SourceSyncPolicyBody as SyncPolicyBody, +} from './source-models' import { Button } from '@langgenius/dify-ui/button' import { Checkbox } from '@langgenius/dify-ui/checkbox' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' @@ -17,11 +17,9 @@ import { useRouter } from '@/next/navigation' import { consoleClient, consoleQuery } from '@/service/client' import { createRequestId } from './request-id' import { newKnowledgeDetailPath } from './routes' +import { sourceSyncPolicyFromApi, sourceWorkflowFromApi } from './source-models' -type PreviewPage = GetKnowledgeSpacesByIdSourceWorkflowsByRunIdPagesResponse['items'][number] -type SyncPolicy = GetKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyResponse type SyncMode = SyncPolicy['mode'] -type SyncPolicyBody = PutKnowledgeSpacesByIdSourcesBySourceIdSyncPolicyData['body'] const MIN_CUSTOM_INTERVAL_HOURS = 1 const MAX_CUSTOM_INTERVAL_HOURS = 720 @@ -78,9 +76,11 @@ async function waitForImportTerminal( let current = initialRun for (let attempt = 0; attempt < IMPORT_POLL_ATTEMPTS; attempt += 1) { if (discardRequested() || isTerminalImport(current)) return current - current = await consoleClient.knowledgeFs.getKnowledgeSpacesByIdSourceWorkflowsByRunId({ - params: { id: knowledgeSpaceId, runId: current.id }, - }) + current = sourceWorkflowFromApi( + await consoleClient.knowledgeFs.spaces.byControlSpaceId.sourceWorkflows.byRunId.get({ + params: { control_space_id: knowledgeSpaceId, run_id: current.id }, + }), + ) onWorkflowRun(current) if (discardRequested() || isTerminalImport(current)) return current await new Promise((resolve) => setTimeout(resolve, IMPORT_POLL_INTERVAL_MS)) @@ -226,12 +226,35 @@ function ReadyCrawlSelectionForm({ const selectionRequestRef = useRef<{ fingerprint: string; requestId: string } | undefined>( undefined, ) - const updatePolicy = useMutation( - consoleQuery.knowledgeFs.putKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy.mutationOptions(), - ) - const selectPages = useMutation( - consoleQuery.knowledgeFs.postKnowledgeSpacesByIdSourceWorkflowsByRunIdSelection.mutationOptions(), - ) + const updatePolicy = useMutation({ + mutationFn: async ({ body, sourceId }: { body: SyncPolicyBody; sourceId: string }) => + sourceSyncPolicyFromApi( + await consoleClient.knowledgeFs.spaces.byControlSpaceId.sources.bySourceId.syncPolicy.put({ + body, + params: { control_space_id: knowledgeSpaceId, source_id: sourceId }, + }), + ), + }) + const selectPages = useMutation({ + mutationFn: async ({ + idempotencyKey, + pageIds, + runId, + }: { + idempotencyKey: string + pageIds: string[] + runId: string + }) => + sourceWorkflowFromApi( + await consoleClient.knowledgeFs.spaces.byControlSpaceId.sourceWorkflows.byRunId.selection.post( + { + body: { pageIds }, + headers: { 'Idempotency-Key': idempotencyKey }, + params: { control_space_id: knowledgeSpaceId, run_id: runId }, + }, + ), + ), + }) const allSelected = bulkSelectablePages.length > 0 && bulkSelectablePages.every((page) => selectedPageIds.has(page.pageId)) @@ -322,15 +345,21 @@ function ReadyCrawlSelectionForm({ try { currentPolicy = await updatePolicy.mutateAsync({ body, - params: { id: knowledgeSpaceId, sourceId: source.id }, + sourceId: source.id, }) } catch (error) { let reconciled: SyncPolicy try { - reconciled = - await consoleClient.knowledgeFs.getKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy({ - params: { id: knowledgeSpaceId, sourceId: source.id }, - }) + reconciled = sourceSyncPolicyFromApi( + await consoleClient.knowledgeFs.spaces.byControlSpaceId.sources.bySourceId.syncPolicy.get( + { + params: { + control_space_id: knowledgeSpaceId, + source_id: source.id, + }, + }, + ), + ) } catch (reconciliationError) { setPolicyUncertain(!isDefinitiveRequestFailure(error)) throw reconciliationError @@ -350,9 +379,9 @@ function ReadyCrawlSelectionForm({ try { const selectionRequest = selectPages.mutateAsync({ - body: { pageIds: sortedPageIds }, - headers: { 'Idempotency-Key': selectionRequestRef.current.requestId }, - params: { id: knowledgeSpaceId, runId: run.id }, + idempotencyKey: selectionRequestRef.current.requestId, + pageIds: sortedPageIds, + runId: run.id, }) const selectionRun = await selectionRequest transactionRun = selectionRun @@ -378,7 +407,7 @@ function ReadyCrawlSelectionForm({ } updateSelectionUncertain(false) await queryClient.invalidateQueries({ - queryKey: consoleQuery.knowledgeFs.getKnowledgeSpacesByIdSources.key(), + queryKey: consoleQuery.knowledgeFs.spaces.byControlSpaceId.sources.get.key(), }) if (discardRequested()) return await onSubmitted() @@ -603,11 +632,19 @@ export function CrawlSelectionForm({ }) { const { t } = useTranslation('dataset') const policyQuery = useQuery( - consoleQuery.knowledgeFs.getKnowledgeSpacesByIdSourcesBySourceIdSyncPolicy.queryOptions({ - context: { silent: true }, - input: { params: { id: knowledgeSpaceId, sourceId: source.id } }, - retry: false, - }), + consoleQuery.knowledgeFs.spaces.byControlSpaceId.sources.bySourceId.syncPolicy.get.queryOptions( + { + context: { silent: true }, + input: { + params: { + control_space_id: knowledgeSpaceId, + source_id: source.id, + }, + }, + retry: false, + select: sourceSyncPolicyFromApi, + }, + ), ) const policy = policyQuery.data ?? diff --git a/web/features/new-rag/create-knowledge-page.tsx b/web/features/new-rag/create-knowledge-page.tsx index e8f290789e1..a353be9c984 100644 --- a/web/features/new-rag/create-knowledge-page.tsx +++ b/web/features/new-rag/create-knowledge-page.tsx @@ -1,9 +1,10 @@ 'use client' -import type { KnowledgeSpaceCreationResponse } from '@dify/contracts/knowledge-fs/types.gen' +import type { KnowledgeFsSpaceCreateResponse } from '@dify/contracts/api/console/knowledge-fs/types.gen' import type { CreateKnowledgeExitReason } from './components/create-knowledge-exit-dialog' import type { KnowledgeVisibility } from './create-knowledge-workflow' import type { QueuedUpload } from './create-upload-queue' +import type { KnowledgeFsUploadProgress } from './knowledge-fs-upload' import type { NewKnowledgeSourceDraft, NewKnowledgeStartMode } from './routes' import { Button } from '@langgenius/dify-ui/button' import { @@ -38,8 +39,9 @@ import { useAtomValue } from 'jotai' import { useCallback, useEffect, useId, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { workspacePermissionKeysAtom } from '@/context/permission-state' +import { knowledgeFsUploadEnabledAtom } from '@/context/system-features-state' import { useRouter, useSearchParams } from '@/next/navigation' -import { consoleClient, consoleQuery } from '@/service/client' +import { consoleQuery } from '@/service/client' import { DatasetACLPermission, hasPermission } from '@/utils/permission' import { KnowledgeIllustration, StartMode } from './components/create-knowledge-dialog-parts' import { CreateKnowledgeExitDialog } from './components/create-knowledge-exit-dialog' @@ -52,6 +54,7 @@ import { } from './create-knowledge-workflow' import { CreateSourceSetup } from './create-source-setup' import { CreateUploadQueue } from './create-upload-queue' +import { uploadKnowledgeFsDocuments } from './knowledge-fs-upload' import { createRequestId } from './request-id' import { createNewKnowledgeSourceDraft, @@ -68,23 +71,6 @@ function normalizeStartMode(value: string | null): NewKnowledgeStartMode { return 'empty' } -async function uploadCreatedDocuments(knowledgeSpaceId: string, files: File[]) { - if (files.length === 1) { - await consoleClient.knowledgeFs.postKnowledgeSpacesByIdDocuments({ - body: { file: files[0]! }, - params: { id: knowledgeSpaceId }, - }) - return - } - - const result = await consoleClient.knowledgeFs.postKnowledgeSpacesByIdDocumentsBulk({ - body: { files }, - params: { id: knowledgeSpaceId }, - }) - if (!result.accepted) throw new Error('No files were accepted') - return result -} - export function CreateKnowledgePage() { const { t } = useTranslation('dataset') const { t: tCommon } = useTranslation('common') @@ -94,15 +80,18 @@ export function CreateKnowledgePage() { const dialogTitleId = useId() const permissionDescriptionId = useId() const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom) + const uploadAvailable = useAtomValue(knowledgeFsUploadEnabledAtom) const canConfigureAccess = hasPermission( workspacePermissionKeys, DatasetACLPermission.AccessConfig, ) - const defaultVisibility: KnowledgeVisibility = canConfigureAccess ? 'all_members' : 'only_me' + const defaultVisibility: KnowledgeVisibility = canConfigureAccess ? 'all_team_members' : 'only_me' const [name, setName] = useState('') const [description, setDescription] = useState('') const [visibility, setVisibility] = useState(defaultVisibility) - const initialStartMode = normalizeStartMode(searchParams.get('start')) + const requestedStartMode = normalizeStartMode(searchParams.get('start')) + const initialStartMode = + requestedStartMode === 'upload' && !uploadAvailable ? 'empty' : requestedStartMode const [startMode, setStartMode] = useState(initialStartMode) const [sourceDraft, setSourceDraft] = useState(() => createNewKnowledgeSourceDraft('websiteCrawl'), @@ -111,19 +100,21 @@ export function CreateKnowledgePage() { Partial> >({}) const [uploads, setUploads] = useState([]) - const [createdKnowledge, setCreatedKnowledge] = useState() + const [createdKnowledge, setCreatedKnowledge] = useState() const [submissionLocked, setSubmissionLocked] = useState(false) const [uploading, setUploading] = useState(false) const [uploadError, setUploadError] = useState(false) const [exitReason, setExitReason] = useState(null) const idempotencyKeyRef = useRef(undefined) + const uploadProgressRef = useRef(new Map()) const historyGuardArmedRef = useRef(false) const browserBackExitRef = useRef(false) const pendingNavigationRef = useRef(undefined) const createMutation = useMutation({ mutationFn: createKnowledge }) const submissionPending = createMutation.isPending || uploading const uploadSubmissionBlocked = - startMode === 'upload' && (!uploads.length || uploads.some((upload) => upload.issue)) + startMode === 'upload' && + (!uploadAvailable || !uploads.length || uploads.some((upload) => upload.issue)) const sourceSubmissionBlocked = startMode === 'source' && (sourceDraft.sourceType === 'websiteCrawl' @@ -235,7 +226,7 @@ export function CreateKnowledgePage() { setExitReason(null) if (confirmedReason === 'partial' && createdKnowledge) { browserBackExitRef.current = false - replaceAfterHistoryGuard(newKnowledgeDetailPath(createdKnowledge.id)) + replaceAfterHistoryGuard(newKnowledgeDetailPath(createdKnowledge.control_space_id)) return } browserBackExitRef.current = false @@ -268,7 +259,7 @@ export function CreateKnowledgePage() { onCreated: (knowledgeSpace) => { setCreatedKnowledge(knowledgeSpace) void queryClient.invalidateQueries({ - queryKey: consoleQuery.knowledgeFs.listKnowledgeSpaces.key(), + queryKey: consoleQuery.knowledgeFs.spaces.get.key(), }) }, visibility, @@ -277,21 +268,11 @@ export function CreateKnowledgePage() { setUploading(true) setUploadError(false) try { - const result = await uploadCreatedDocuments( - created.id, - uploads.map((upload) => upload.file), + await uploadKnowledgeFsDocuments( + created.control_space_id, + uploads.map(({ file, id }) => ({ file, id })), + uploadProgressRef.current, ) - if (result?.excluded) - toast.warning( - t(($) => $['newKnowledge.documentUploadPartial'], { - accepted: result.accepted, - details: result.items - .filter((item) => 'reason' in item) - .map((item) => item.filename) - .join(', '), - excluded: result.excluded, - }), - ) } catch { setUploadError(true) return @@ -308,7 +289,11 @@ export function CreateKnowledgePage() { JSON.stringify(sourceDraft), ) replaceAfterHistoryGuard( - newKnowledgeAddSourcePath(created.id, sourceDraft.sourceType, sourceDraftKey), + newKnowledgeAddSourcePath( + created.control_space_id, + sourceDraft.sourceType, + sourceDraftKey, + ), ) } catch { toast.error(t(($) => $['newKnowledge.addSourceFailed'])) @@ -318,17 +303,13 @@ export function CreateKnowledgePage() { replaceAfterHistoryGuard( startMode === 'upload' - ? newKnowledgeDocumentsPath(created.id) - : newKnowledgeDetailPath(created.id), + ? newKnowledgeDocumentsPath(created.control_space_id) + : newKnowledgeDetailPath(created.control_space_id), ) } catch (error) { - if (error instanceof KnowledgeCreationError && error.createdKnowledge) - setCreatedKnowledge(error.createdKnowledge) - if ( error instanceof KnowledgeCreationError && - error.stage === 'create' && - isDefinitiveCreationRejection(error.originalError) + (error.stage === 'preflight' || isDefinitiveCreationRejection(error.originalError)) ) { idempotencyKeyRef.current = undefined setSubmissionLocked(false) @@ -440,7 +421,7 @@ export function CreateKnowledgePage() { aria-describedby={!canConfigureAccess ? permissionDescriptionId : undefined} > {t(($) => - visibility === 'all_members' + visibility === 'all_team_members' ? $['newKnowledge.permissionAllMembers'] : $['newKnowledge.permissionOnlyMe'], )} @@ -452,7 +433,7 @@ export function CreateKnowledgePage() { - + {t(($) => $['newKnowledge.permissionAllMembers'])} @@ -524,6 +505,7 @@ export function CreateKnowledgePage() { value="upload" icon="i-ri-file-text-line" selected={startMode === 'upload'} + disabled={!uploadAvailable} title={t(($) => $['newKnowledge.uploadFiles'])} description={t(($) => $['newKnowledge.uploadFilesDescription'])} > @@ -545,12 +527,7 @@ export function CreateKnowledgePage() { className="mt-5 rounded-lg bg-components-badge-status-light-error-bg px-3 py-2 system-sm-regular text-text-destructive" role="alert" > - {t(($) => - createMutation.error instanceof KnowledgeCreationError && - createMutation.error.stage === 'policy' - ? $['newKnowledge.permissionUpdateFailed'] - : $['newKnowledge.createFailed'], - )} + {t(($) => $['newKnowledge.createFailed'])}
)} {uploadError && ( diff --git a/web/features/new-rag/create-knowledge-workflow.ts b/web/features/new-rag/create-knowledge-workflow.ts index c77f07cacd6..8368c3cfef7 100644 --- a/web/features/new-rag/create-knowledge-workflow.ts +++ b/web/features/new-rag/create-knowledge-workflow.ts @@ -1,35 +1,37 @@ -import type { KnowledgeSpaceCreationResponse } from '@dify/contracts/knowledge-fs/types.gen' +import type { + KnowledgeFsControlSpaceVisibility, + KnowledgeFsModelIntent, + KnowledgeFsSpaceCreatePayload, + KnowledgeFsSpaceCreateResponse, +} from '@dify/contracts/api/console/knowledge-fs/types.gen' import { consoleClient } from '@/service/client' export const NAME_MAX_LENGTH = 160 export const DESCRIPTION_MAX_LENGTH = 2000 -export type KnowledgeVisibility = 'all_members' | 'only_me' +export type KnowledgeVisibility = Extract< + KnowledgeFsControlSpaceVisibility, + 'all_team_members' | 'only_me' +> type CreateKnowledgeValues = { - existingKnowledge?: KnowledgeSpaceCreationResponse + existingKnowledge?: KnowledgeFsSpaceCreateResponse description: string idempotencyKey: string name: string - onCreated: (knowledgeSpace: KnowledgeSpaceCreationResponse) => void + onCreated: (knowledgeSpace: KnowledgeFsSpaceCreateResponse) => void visibility: KnowledgeVisibility } export class KnowledgeCreationError extends Error { - readonly stage: 'create' | 'policy' readonly originalError: unknown - readonly createdKnowledge?: KnowledgeSpaceCreationResponse + readonly stage: 'preflight' | 'request' - constructor( - stage: 'create' | 'policy', - originalError: unknown, - createdKnowledge?: KnowledgeSpaceCreationResponse, - ) { - super(`Knowledge creation failed during ${stage}`) + constructor(originalError: unknown, stage: 'preflight' | 'request') { + super('Knowledge creation failed') this.name = 'KnowledgeCreationError' - this.stage = stage this.originalError = originalError - this.createdKnowledge = createdKnowledge + this.stage = stage } } @@ -47,44 +49,88 @@ export function isDefinitiveCreationRejection(error: unknown) { return status === 400 || status === 401 || status === 403 || status === 422 } +function modelSelection(model: string, canonicalProvider: string): KnowledgeFsModelIntent { + const providerParts = canonicalProvider.split('/').filter(Boolean) + const provider = providerParts.pop() + const pluginId = providerParts.join('/') + if (!model.trim() || !pluginId || !provider) + throw new Error('The default model provider identity is incomplete') + + return { model, plugin_id: pluginId, provider } +} + +async function getDefaultModelSelection( + modelType: 'llm' | 'text-embedding', +): Promise { + const response = await consoleClient.workspaces.current.defaultModel.get({ + query: { model_type: modelType }, + }) + if (!response.data) return undefined + return modelSelection(response.data.model, response.data.provider.provider) +} + +async function defaultModelConfiguration(): Promise< + Pick +> { + const [embedding, reasoningModel] = await Promise.all([ + getDefaultModelSelection('text-embedding'), + getDefaultModelSelection('llm'), + ]) + if (!embedding) throw new Error('A default embedding model is required') + if (!reasoningModel) throw new Error('A default reasoning model is required') + + return { + embedding, + retrieval: { + default_mode: 'fast', + reasoning_model: reasoningModel, + rerank: { enabled: false }, + score_threshold: { enabled: false, stage: 'mode-final' }, + top_k: 10, + }, + } +} + +function knowledgeSlug(name: string, idempotencyKey: string) { + const normalizedName = name + .normalize('NFKD') + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-|-$/g, '') + const suffix = idempotencyKey + .toLowerCase() + .replace(/[^a-z0-9]/g, '') + .slice(0, 12) + const base = normalizedName || 'knowledge' + return `${base.slice(0, 147 - suffix.length)}-${suffix}` +} + export async function createKnowledge( values: CreateKnowledgeValues, -): Promise { +): Promise { let created = values.existingKnowledge if (!created) { + let modelConfiguration: Pick try { - created = await consoleClient.knowledgeFs.createKnowledgeSpace({ + modelConfiguration = await defaultModelConfiguration() + } catch (error) { + throw new KnowledgeCreationError(error, 'preflight') + } + try { + created = await consoleClient.knowledgeFs.spaces.post({ body: { description: values.description || undefined, - idempotencyKey: values.idempotencyKey, + idempotency_key: values.idempotencyKey, + ...modelConfiguration, name: values.name, + slug: knowledgeSlug(values.name, values.idempotencyKey), + visibility: values.visibility, }, }) } catch (error) { - throw new KnowledgeCreationError('create', error) + throw new KnowledgeCreationError(error, 'request') } } values.onCreated(created) - - try { - if (values.visibility === 'all_members') { - const policy = await consoleClient.knowledgeFs.getKnowledgeSpacesByIdAccessPolicy({ - params: { id: created.id }, - }) - if (policy.visibility !== values.visibility) { - await consoleClient.knowledgeFs.patchKnowledgeSpacesByIdAccessPolicy({ - body: { - expectedRevision: policy.revision, - partialMemberSubjectIds: [], - visibility: values.visibility, - }, - params: { id: created.id }, - }) - } - } - } catch (error) { - throw new KnowledgeCreationError('policy', error, created) - } - return created } diff --git a/web/features/new-rag/create-source-setup.tsx b/web/features/new-rag/create-source-setup.tsx index d573329a702..20e00b3426b 100644 --- a/web/features/new-rag/create-source-setup.tsx +++ b/web/features/new-rag/create-source-setup.tsx @@ -22,6 +22,9 @@ const sourceTypes = [ { icon: 'i-ri-hard-drive-3-line', value: 'onlineDrive' }, ] as const +const DEFAULT_INCLUDE_SUBPAGES = true +const DEFAULT_MAX_PAGES = 100 + const providers = { onlineDocuments: [ { icon: 'i-custom-public-common-notion', label: 'Notion' }, @@ -117,6 +120,9 @@ export function CreateSourceSetup({ ? draft.provider : availableProviders[0].label const previewReady = draft.sourceType === 'websiteCrawl' && isValidWebsiteSourceDraft(draft) + const crawlOptionsAreDefault = + draft.sourceType !== 'websiteCrawl' || + (draft.includeSubpages === DEFAULT_INCLUDE_SUBPAGES && draft.maxPages === DEFAULT_MAX_PAGES) const showBackendBoundary = () => setBackendBoundaryVisible(true) const updateDraft = (nextDraft: NewKnowledgeSourceDraft) => { onDraftChange(nextDraft) @@ -273,7 +279,13 @@ export function CreateSourceSetup({ {t(($) => $['newKnowledge.crawlOptions'])} {!optionsExpanded && ( - {t(($) => $['newKnowledge.usingDefaults'])} + {crawlOptionsAreDefault + ? t(($) => $['newKnowledge.usingDefaults']) + : `${t(($) => $['newKnowledge.includeSubpages'])}: ${t(($) => + draft.includeSubpages + ? $['newKnowledge.booleanTrue'] + : $['newKnowledge.booleanFalse'], + )} · ${t(($) => $['newKnowledge.maxPages'])}: ${draft.maxPages}`} )} diff --git a/web/features/new-rag/create-upload-queue.tsx b/web/features/new-rag/create-upload-queue.tsx index 9a2523fc230..c53de828387 100644 --- a/web/features/new-rag/create-upload-queue.tsx +++ b/web/features/new-rag/create-upload-queue.tsx @@ -21,7 +21,7 @@ export type QueuedUpload = { function createQueuedUpload(file: File): QueuedUpload { return { file, - id: `${file.name}:${file.size}:${file.lastModified}:${createRequestId()}`, + id: createRequestId(), issue: documentUploadIssue(file), } } diff --git a/web/features/new-rag/document-chunk-detail.tsx b/web/features/new-rag/document-chunk-detail.tsx index 1c770a31d66..8a6ba509fb5 100644 --- a/web/features/new-rag/document-chunk-detail.tsx +++ b/web/features/new-rag/document-chunk-detail.tsx @@ -2,7 +2,7 @@ import type { DocumentRevisionChunk, LogicalDocument, LogicalDocumentRevision, -} from '@dify/contracts/knowledge-fs/types.gen' +} from './document-models' import { Button } from '@langgenius/dify-ui/button' import { toast } from '@langgenius/dify-ui/toast' import copy from 'copy-to-clipboard' diff --git a/web/features/new-rag/document-detail-header.tsx b/web/features/new-rag/document-detail-header.tsx index 77a591bc728..88c046b462a 100644 --- a/web/features/new-rag/document-detail-header.tsx +++ b/web/features/new-rag/document-detail-header.tsx @@ -1,8 +1,5 @@ -import type { - LogicalDocument, - LogicalDocumentRevision, -} from '@dify/contracts/knowledge-fs/types.gen' import type { RefObject } from 'react' +import type { LogicalDocument, LogicalDocumentRevision } from './document-models' import { Button } from '@langgenius/dify-ui/button' import { Select, diff --git a/web/features/new-rag/document-detail-model.ts b/web/features/new-rag/document-detail-model.ts index dbbfa7170d4..58ff018f812 100644 --- a/web/features/new-rag/document-detail-model.ts +++ b/web/features/new-rag/document-detail-model.ts @@ -2,7 +2,7 @@ import type { DocumentRevisionChunk, LogicalDocument, LogicalDocumentRevision, -} from '@dify/contracts/knowledge-fs/types.gen' +} from './document-models' export type DocumentChunkTreeNode = { children: DocumentChunkTreeNode[] diff --git a/web/features/new-rag/document-detail-page.tsx b/web/features/new-rag/document-detail-page.tsx index 372dc4a7a9a..c8457b5dd51 100644 --- a/web/features/new-rag/document-detail-page.tsx +++ b/web/features/new-rag/document-detail-page.tsx @@ -13,11 +13,11 @@ import { DatasetACLPermission, hasPermission } from '@/utils/permission' import { DocumentDetailHeader } from './document-detail-header' import { initialDocumentRevision, responseStatus } from './document-detail-model' import { DocumentDetailStatus } from './document-detail-status' +import { documentRevisionListFromApi, logicalDocumentFromApi } from './document-models' import { DocumentRevisionContent } from './document-revision-content' import { newKnowledgeDocumentsPath } from './routes' import { useDocumentReindex } from './use-document-reindex' -const REVISION_PAGE_SIZE = 50 const REINDEX_RESTRICTION_ID = 'document-reindex-restriction' const documentRevisionParser = createParser({ parse: (value) => { @@ -66,28 +66,38 @@ export function DocumentDetailPage({ const documentQueryOptions = useMemo( () => - consoleQuery.knowledgeFs.getKnowledgeSpacesByIdLogicalDocumentsByDocumentId.queryOptions({ - input: { params: { documentId, id: knowledgeSpaceId } }, - retry: (failureCount, error) => { - const status = responseStatus(error) - return status !== 403 && status !== 404 && failureCount < 2 + consoleQuery.knowledgeFs.spaces.byControlSpaceId.logicalDocuments.byDocumentId.get.queryOptions( + { + input: { + params: { + control_space_id: knowledgeSpaceId, + document_id: documentId, + }, + }, + retry: (failureCount, error) => { + const status = responseStatus(error) + return status !== 403 && status !== 404 && failureCount < 2 + }, + select: logicalDocumentFromApi, }, - }), + ), [documentId, knowledgeSpaceId], ) const documentQuery = useQuery(documentQueryOptions) const revisionsQueryOptions = useMemo( () => - consoleQuery.knowledgeFs.getKnowledgeSpacesByIdDocumentsByDocumentIdRevisions.infiniteOptions( + consoleQuery.knowledgeFs.spaces.byControlSpaceId.documents.byDocumentId.revisions.get.infiniteOptions( { input: (pageParam) => ({ - params: { documentId, id: knowledgeSpaceId }, + params: { + control_space_id: knowledgeSpaceId, + document_id: documentId, + }, query: { - limit: REVISION_PAGE_SIZE, ...(typeof pageParam === 'string' ? { cursor: pageParam } : {}), }, }), - getNextPageParam: (lastPage) => lastPage.nextCursor, + getNextPageParam: (lastPage) => lastPage.next_cursor, initialPageParam: null as string | null, }, ), @@ -95,7 +105,8 @@ export function DocumentDetailPage({ ) const revisionsQuery = useInfiniteQuery(revisionsQueryOptions) const revisions = useMemo( - () => revisionsQuery.data?.pages.flatMap((page) => page.items).filter(Boolean) ?? [], + () => + revisionsQuery.data?.pages.flatMap((page) => documentRevisionListFromApi(page).items) ?? [], [revisionsQuery.data], ) const availableRevisions = useMemo(() => { @@ -108,7 +119,7 @@ export function DocumentDetailPage({ ? (selectedRevision ?? initialDocumentRevision(documentQuery.data, availableRevisions)) : undefined const chunksQueryKey = - consoleQuery.knowledgeFs.getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunks.key() + consoleQuery.knowledgeFs.spaces.byControlSpaceId.documents.byDocumentId.revisions.byRevision.chunks.get.key() const documentActiveRevision = documentQuery.data?.activeRevision ?? documentQuery.data?.active?.revision ?? 0 const documentErrorStatus = responseStatus(documentQuery.error) diff --git a/web/features/new-rag/document-detail-queries.ts b/web/features/new-rag/document-detail-queries.ts index c504e8229c3..440a49f1982 100644 --- a/web/features/new-rag/document-detail-queries.ts +++ b/web/features/new-rag/document-detail-queries.ts @@ -1,7 +1,5 @@ import { consoleQuery } from '@/service/client' -const CHUNK_PAGE_SIZE = 100 - export function documentChunksQueryOptions({ documentId, effectiveRevision, @@ -12,17 +10,21 @@ export function documentChunksQueryOptions({ knowledgeSpaceId: string }) { const chunksQuery = - consoleQuery.knowledgeFs.getKnowledgeSpacesByIdDocumentsByDocumentIdRevisionsByRevisionChunks + consoleQuery.knowledgeFs.spaces.byControlSpaceId.documents.byDocumentId.revisions.byRevision + .chunks - return chunksQuery.infiniteOptions({ + return chunksQuery.get.infiniteOptions({ input: (pageParam) => ({ - params: { documentId, id: knowledgeSpaceId, revision: effectiveRevision }, + params: { + control_space_id: knowledgeSpaceId, + document_id: documentId, + revision: effectiveRevision, + }, query: { - limit: CHUNK_PAGE_SIZE, ...(typeof pageParam === 'string' ? { cursor: pageParam } : {}), }, }), - getNextPageParam: (lastPage) => lastPage.nextCursor, + getNextPageParam: (lastPage) => lastPage.next_cursor, initialPageParam: null as string | null, }) } diff --git a/web/features/new-rag/document-detail-status.tsx b/web/features/new-rag/document-detail-status.tsx index 23c2118f6f8..a4c2862ad5d 100644 --- a/web/features/new-rag/document-detail-status.tsx +++ b/web/features/new-rag/document-detail-status.tsx @@ -1,5 +1,5 @@ -import type { DocumentProcessingTask } from '@dify/contracts/knowledge-fs/types.gen' import type { RefObject } from 'react' +import type { DocumentProcessingTask } from './document-models' import { Button } from '@langgenius/dify-ui/button' import { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' diff --git a/web/features/new-rag/document-list.tsx b/web/features/new-rag/document-list.tsx index 780313270fe..14af6684751 100644 --- a/web/features/new-rag/document-list.tsx +++ b/web/features/new-rag/document-list.tsx @@ -1,8 +1,8 @@ 'use client' -import type { LogicalDocument } from '@dify/contracts/knowledge-fs/types.gen' import type { FocusEventHandler } from 'react' import type { DocumentDisplayStatus } from './document-model' +import type { LogicalDocument } from './document-models' import { Button } from '@langgenius/dify-ui/button' import { Checkbox } from '@langgenius/dify-ui/checkbox' import { cn } from '@langgenius/dify-ui/cn' @@ -310,6 +310,7 @@ export function DocumentsList({ allSelected, attentionTaskBadge, canEdit, + canUpload, completingResults, documents, filter, @@ -340,12 +341,14 @@ export function DocumentsList({ tasksPending, tasksButtonLabel, tasksLiveStatus, + uploadRestrictionReasonId, uploading, }: { activeTaskCount: number allSelected: boolean attentionTaskBadge?: string canEdit: boolean + canUpload: boolean completingResults: boolean documents: LogicalDocument[] filter: DocumentFilter @@ -376,6 +379,7 @@ export function DocumentsList({ tasksPending: boolean tasksButtonLabel: string tasksLiveStatus: string + uploadRestrictionReasonId?: string uploading: boolean }) { const { t } = useTranslation('dataset') @@ -455,9 +459,9 @@ export function DocumentsList({ @@ -1219,7 +1264,7 @@ export function WebsiteCrawlPreview({ )} {showSuccess && run && draftRef.current?.source && configuration && ( discardRequestedRef.current} initialSyncMode={ initialDraft?.syncPolicy === 'daily' ? 'interval' : initialDraft?.syncPolicy diff --git a/web/i18n/ar-TN/dataset.json b/web/i18n/ar-TN/dataset.json index dd57a25022b..c1601c288ac 100644 --- a/web/i18n/ar-TN/dataset.json +++ b/web/i18n/ar-TN/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "اربط Notion لاختيار الصفحات والحفاظ على مزامنتها.", "newKnowledge.onlineDocuments": "المستندات عبر الإنترنت", "newKnowledge.onlineDrive": "محرك الأقراص عبر الإنترنت", + "newKnowledge.openDataSourceSettings": "فتح إعدادات مصادر البيانات", "newKnowledge.overview": "نظرة عامة", "newKnowledge.pagesAppearDescription": "ازحف إلى الموقع لمراجعة الصفحات التي ستتم إضافتها.", "newKnowledge.pagesAppearTitle": "ستظهر الصفحات هنا", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "مكتمل", "newKnowledge.processingTaskState.superseded": "تم استبداله", "newKnowledge.providerConnected": "تم توصيل {{provider}}", + "newKnowledge.providerCredentialRequiredDescription": "قم بإعداد بيانات اعتماد {{provider}} في مصادر البيانات، ثم ارجع إلى هنا للاتصال.", "newKnowledge.providerLabel": "اختر مزودًا", "newKnowledge.providerLoadFailed": "لم يتمكن الموفرون من التحميل", "newKnowledge.providerNotConfigured": "{{provider}} لم يتم تكوينه", diff --git a/web/i18n/de-DE/dataset.json b/web/i18n/de-DE/dataset.json index 846a4ea7347..5e4546c9211 100644 --- a/web/i18n/de-DE/dataset.json +++ b/web/i18n/de-DE/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Verbinden Sie Notion, um Seiten auszuwählen und synchron zu halten.", "newKnowledge.onlineDocuments": "Online-Dokumente", "newKnowledge.onlineDrive": "Online-Laufwerk", + "newKnowledge.openDataSourceSettings": "Datenquelleneinstellungen öffnen", "newKnowledge.overview": "Übersicht", "newKnowledge.pagesAppearDescription": "Crawle die Website, um zu prüfen, welche Seiten hinzugefügt werden.", "newKnowledge.pagesAppearTitle": "Seiten werden hier angezeigt", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Abgeschlossen", "newKnowledge.processingTaskState.superseded": "Abgelöst", "newKnowledge.providerConnected": "{{provider}} verbunden", + "newKnowledge.providerCredentialRequiredDescription": "Konfigurieren Sie in den Datenquellen Anmeldedaten für {{provider}} und kehren Sie dann hierher zurück, um die Verbindung herzustellen.", "newKnowledge.providerLabel": "Wählen Sie einen Anbieter", "newKnowledge.providerLoadFailed": "Anbieter konnten nicht geladen werden", "newKnowledge.providerNotConfigured": "{{provider}} ist nicht konfiguriert", diff --git a/web/i18n/en-US/dataset.json b/web/i18n/en-US/dataset.json index b8842171079..435bff4f7ca 100644 --- a/web/i18n/en-US/dataset.json +++ b/web/i18n/en-US/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Connect Notion to choose pages and keep them synchronized.", "newKnowledge.onlineDocuments": "Online documents", "newKnowledge.onlineDrive": "Online drive", + "newKnowledge.openDataSourceSettings": "Open Data Source settings", "newKnowledge.overview": "Overview", "newKnowledge.pagesAppearDescription": "Crawl the site to review which pages get added.", "newKnowledge.pagesAppearTitle": "Pages will appear here", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Completed", "newKnowledge.processingTaskState.superseded": "Superseded", "newKnowledge.providerConnected": "{{provider}} connected", + "newKnowledge.providerCredentialRequiredDescription": "Configure a {{provider}} credential in Data Sources, then return here to connect it.", "newKnowledge.providerLabel": "Provider", "newKnowledge.providerLoadFailed": "Providers couldn't load", "newKnowledge.providerNotConfigured": "{{provider}} is not configured", @@ -385,7 +387,7 @@ "newKnowledge.sourceName": "Source name", "newKnowledge.sourceNamePlaceholder": "e.g. Product docs crawl", "newKnowledge.sourceNameRequired": "Enter a source name.", - "newKnowledge.sourceSetupBackendDependency": "Source setup is shown in full; editing and preview actions require the draft source backend workflow.", + "newKnowledge.sourceSetupBackendDependency": "This source type or provider is not available in the current KnowledgeFS backend.", "newKnowledge.sourceStatus.active": "Active", "newKnowledge.sourceStatus.disabled": "Disabled", "newKnowledge.sourceStatus.error": "Error", diff --git a/web/i18n/es-ES/dataset.json b/web/i18n/es-ES/dataset.json index 8eef442d1fb..f723c142bf7 100644 --- a/web/i18n/es-ES/dataset.json +++ b/web/i18n/es-ES/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Conecta Notion para elegir páginas y mantenerlas sincronizadas.", "newKnowledge.onlineDocuments": "Documentos en línea", "newKnowledge.onlineDrive": "Unidad en línea", + "newKnowledge.openDataSourceSettings": "Abrir configuración de fuentes de datos", "newKnowledge.overview": "Resumen", "newKnowledge.pagesAppearDescription": "Rastrea el sitio para revisar qué páginas se añadirán.", "newKnowledge.pagesAppearTitle": "Las páginas aparecerán aquí", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Completado", "newKnowledge.processingTaskState.superseded": "Reemplazado", "newKnowledge.providerConnected": "{{provider}} conectado", + "newKnowledge.providerCredentialRequiredDescription": "Configura una credencial de {{provider}} en Fuentes de datos y vuelve aquí para conectarla.", "newKnowledge.providerLabel": "Seleccione un proveedor", "newKnowledge.providerLoadFailed": "Los proveedores no pudieron cargar", "newKnowledge.providerNotConfigured": "{{provider}} no está configurado", diff --git a/web/i18n/fa-IR/dataset.json b/web/i18n/fa-IR/dataset.json index a678ec89324..b83d1da5434 100644 --- a/web/i18n/fa-IR/dataset.json +++ b/web/i18n/fa-IR/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "برای انتخاب صفحه‌ها و همگام نگه‌داشتن آن‌ها، Notion را متصل کنید.", "newKnowledge.onlineDocuments": "اسناد آنلاین", "newKnowledge.onlineDrive": "درایو آنلاین", + "newKnowledge.openDataSourceSettings": "باز کردن تنظیمات منبع داده", "newKnowledge.overview": "نمای کلی", "newKnowledge.pagesAppearDescription": "سایت را پیمایش کنید تا صفحه‌هایی را که افزوده می‌شوند بررسی کنید.", "newKnowledge.pagesAppearTitle": "صفحه‌ها اینجا نمایش داده می‌شوند", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "تکمیل شد", "newKnowledge.processingTaskState.superseded": "جایگزین شد", "newKnowledge.providerConnected": "{{provider}} متصل شد", + "newKnowledge.providerCredentialRequiredDescription": "اعتبارنامه {{provider}} را در منابع داده پیکربندی کنید، سپس برای اتصال به اینجا بازگردید.", "newKnowledge.providerLabel": "یک ارائه دهنده را انتخاب کنید", "newKnowledge.providerLoadFailed": "ارائه‌دهندگان نتوانستند بارگیری کنند", "newKnowledge.providerNotConfigured": "{{provider}} پیکربندی نشده است", diff --git a/web/i18n/fr-FR/dataset.json b/web/i18n/fr-FR/dataset.json index 6f6855db438..18ad9b4cd41 100644 --- a/web/i18n/fr-FR/dataset.json +++ b/web/i18n/fr-FR/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Connectez Notion pour choisir des pages et les maintenir synchronisées.", "newKnowledge.onlineDocuments": "Documents en ligne", "newKnowledge.onlineDrive": "Drive en ligne", + "newKnowledge.openDataSourceSettings": "Ouvrir les paramètres des sources de données", "newKnowledge.overview": "Vue d’ensemble", "newKnowledge.pagesAppearDescription": "Explorez le site pour vérifier les pages qui seront ajoutées.", "newKnowledge.pagesAppearTitle": "Les pages apparaîtront ici", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Terminé", "newKnowledge.processingTaskState.superseded": "Remplacé", "newKnowledge.providerConnected": "{{provider}} connecté", + "newKnowledge.providerCredentialRequiredDescription": "Configurez un identifiant {{provider}} dans Sources de données, puis revenez ici pour le connecter.", "newKnowledge.providerLabel": "Sélectionnez un fournisseur", "newKnowledge.providerLoadFailed": "Les fournisseurs n'ont pas pu charger", "newKnowledge.providerNotConfigured": "{{provider}} n'est pas configuré", diff --git a/web/i18n/hi-IN/dataset.json b/web/i18n/hi-IN/dataset.json index b28727a0609..f04c11aa61c 100644 --- a/web/i18n/hi-IN/dataset.json +++ b/web/i18n/hi-IN/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "पृष्ठ चुनने और उन्हें सिंक रखने के लिए Notion कनेक्ट करें।", "newKnowledge.onlineDocuments": "ऑनलाइन दस्तावेज़", "newKnowledge.onlineDrive": "ऑनलाइन ड्राइव", + "newKnowledge.openDataSourceSettings": "डेटा स्रोत सेटिंग खोलें", "newKnowledge.overview": "अवलोकन", "newKnowledge.pagesAppearDescription": "जोड़े जाने वाले पेज देखने के लिए साइट को क्रॉल करें।", "newKnowledge.pagesAppearTitle": "पेज यहाँ दिखाई देंगे", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "पूरा हुआ", "newKnowledge.processingTaskState.superseded": "अधिक्रमण किया हुआ", "newKnowledge.providerConnected": "{{provider}} जुड़ा", + "newKnowledge.providerCredentialRequiredDescription": "डेटा स्रोत में {{provider}} क्रेडेंशियल कॉन्फ़िगर करें, फिर कनेक्ट करने के लिए यहाँ वापस आएँ।", "newKnowledge.providerLabel": "एक प्रदाता का चयन करें", "newKnowledge.providerLoadFailed": "प्रदाता लोड नहीं कर सके", "newKnowledge.providerNotConfigured": "{{provider}} कॉन्फ़िगर नहीं है", diff --git a/web/i18n/id-ID/dataset.json b/web/i18n/id-ID/dataset.json index d1029d7660d..03a9faa8612 100644 --- a/web/i18n/id-ID/dataset.json +++ b/web/i18n/id-ID/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Hubungkan Notion untuk memilih halaman dan menjaganya tetap tersinkron.", "newKnowledge.onlineDocuments": "Dokumen daring", "newKnowledge.onlineDrive": "Perjalanan daring", + "newKnowledge.openDataSourceSettings": "Buka pengaturan Sumber Data", "newKnowledge.overview": "Ringkasan", "newKnowledge.pagesAppearDescription": "Rayapi situs untuk meninjau halaman yang akan ditambahkan.", "newKnowledge.pagesAppearTitle": "Halaman akan muncul di sini", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Selesai", "newKnowledge.processingTaskState.superseded": "Digantikan", "newKnowledge.providerConnected": "{{provider}} terhubung", + "newKnowledge.providerCredentialRequiredDescription": "Konfigurasikan kredensial {{provider}} di Sumber Data, lalu kembali ke sini untuk menghubungkannya.", "newKnowledge.providerLabel": "Pilih penyedia", "newKnowledge.providerLoadFailed": "Penyedia tidak dapat memuat", "newKnowledge.providerNotConfigured": "{{provider}} tidak dikonfigurasi", diff --git a/web/i18n/it-IT/dataset.json b/web/i18n/it-IT/dataset.json index b3d4091dab2..2325845d15e 100644 --- a/web/i18n/it-IT/dataset.json +++ b/web/i18n/it-IT/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Connetti Notion per scegliere le pagine e mantenerle sincronizzate.", "newKnowledge.onlineDocuments": "Documenti online", "newKnowledge.onlineDrive": "Unità in linea", + "newKnowledge.openDataSourceSettings": "Apri le impostazioni delle origini dati", "newKnowledge.overview": "Panoramica", "newKnowledge.pagesAppearDescription": "Esegui la scansione del sito per verificare quali pagine verranno aggiunte.", "newKnowledge.pagesAppearTitle": "Le pagine appariranno qui", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Completato", "newKnowledge.processingTaskState.superseded": "Sostituito", "newKnowledge.providerConnected": "{{provider}} connesso", + "newKnowledge.providerCredentialRequiredDescription": "Configura una credenziale {{provider}} in Origini dati, quindi torna qui per connetterla.", "newKnowledge.providerLabel": "Seleziona un fornitore", "newKnowledge.providerLoadFailed": "Impossibile caricare i provider", "newKnowledge.providerNotConfigured": "{{provider}} non è configurato", diff --git a/web/i18n/ja-JP/dataset.json b/web/i18n/ja-JP/dataset.json index 27193e187e4..694ea7780e1 100644 --- a/web/i18n/ja-JP/dataset.json +++ b/web/i18n/ja-JP/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Notion に接続してページを選択し、同期を維持します。", "newKnowledge.onlineDocuments": "オンライン文書", "newKnowledge.onlineDrive": "オンラインドライブ", + "newKnowledge.openDataSourceSettings": "データソース設定を開く", "newKnowledge.overview": "概要", "newKnowledge.pagesAppearDescription": "サイトをクロールして、追加されるページを確認します。", "newKnowledge.pagesAppearTitle": "ページはここに表示されます", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "完了しました", "newKnowledge.processingTaskState.superseded": "置き換えられました", "newKnowledge.providerConnected": "{{provider}} が接続されました", + "newKnowledge.providerCredentialRequiredDescription": "データソースで {{provider}} の認証情報を設定し、ここに戻って接続してください。", "newKnowledge.providerLabel": "プロバイダーを選択する", "newKnowledge.providerLoadFailed": "プロバイダーを読み込めませんでした", "newKnowledge.providerNotConfigured": "{{provider}} が構成されていません", diff --git a/web/i18n/ko-KR/dataset.json b/web/i18n/ko-KR/dataset.json index a821ff43ec5..0afc62202fb 100644 --- a/web/i18n/ko-KR/dataset.json +++ b/web/i18n/ko-KR/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "페이지를 선택하고 동기화 상태를 유지하려면 Notion을 연결하세요.", "newKnowledge.onlineDocuments": "온라인 문서", "newKnowledge.onlineDrive": "온라인 드라이브", + "newKnowledge.openDataSourceSettings": "데이터 소스 설정 열기", "newKnowledge.overview": "개요", "newKnowledge.pagesAppearDescription": "사이트를 크롤링하여 추가될 페이지를 확인하세요.", "newKnowledge.pagesAppearTitle": "페이지가 여기에 표시됩니다", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "완료됨", "newKnowledge.processingTaskState.superseded": "대체됨", "newKnowledge.providerConnected": "{{provider}} 연결됨", + "newKnowledge.providerCredentialRequiredDescription": "데이터 소스에서 {{provider}} 자격 증명을 구성한 후 여기로 돌아와 연결하세요.", "newKnowledge.providerLabel": "제공자 선택", "newKnowledge.providerLoadFailed": "제공자를 로드할 수 없습니다.", "newKnowledge.providerNotConfigured": "{{provider}}이 구성되지 않았습니다.", diff --git a/web/i18n/nl-NL/dataset.json b/web/i18n/nl-NL/dataset.json index d9b9e53f73c..b1bd85787da 100644 --- a/web/i18n/nl-NL/dataset.json +++ b/web/i18n/nl-NL/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Verbind Notion om pagina’s te kiezen en gesynchroniseerd te houden.", "newKnowledge.onlineDocuments": "Onlinedocumenten", "newKnowledge.onlineDrive": "Online rijden", + "newKnowledge.openDataSourceSettings": "Instellingen voor gegevensbronnen openen", "newKnowledge.overview": "Overzicht", "newKnowledge.pagesAppearDescription": "Crawl de site om te bekijken welke pagina’s worden toegevoegd.", "newKnowledge.pagesAppearTitle": "Pagina’s verschijnen hier", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Voltooid", "newKnowledge.processingTaskState.superseded": "Vervangen", "newKnowledge.providerConnected": "{{provider}} aangesloten", + "newKnowledge.providerCredentialRequiredDescription": "Configureer een {{provider}}-referentie in Gegevensbronnen en kom daarna hier terug om verbinding te maken.", "newKnowledge.providerLabel": "Select a provider", "newKnowledge.providerLoadFailed": "Providers konden niet laden", "newKnowledge.providerNotConfigured": "{{provider}} is niet geconfigureerd", diff --git a/web/i18n/pl-PL/dataset.json b/web/i18n/pl-PL/dataset.json index 9cb709e96c6..263488c1430 100644 --- a/web/i18n/pl-PL/dataset.json +++ b/web/i18n/pl-PL/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Połącz z Notion, aby wybrać strony i utrzymywać ich synchronizację.", "newKnowledge.onlineDocuments": "Dokumenty online", "newKnowledge.onlineDrive": "Dysk online", + "newKnowledge.openDataSourceSettings": "Otwórz ustawienia źródeł danych", "newKnowledge.overview": "Przegląd", "newKnowledge.pagesAppearDescription": "Przeskanuj witrynę, aby sprawdzić, które strony zostaną dodane.", "newKnowledge.pagesAppearTitle": "Strony pojawią się tutaj", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Ukończono", "newKnowledge.processingTaskState.superseded": "Zastąpione", "newKnowledge.providerConnected": "{{provider}} podłączony", + "newKnowledge.providerCredentialRequiredDescription": "Skonfiguruj dane uwierzytelniające {{provider}} w Źródłach danych, a następnie wróć tutaj, aby nawiązać połączenie.", "newKnowledge.providerLabel": "Wybierz dostawcę", "newKnowledge.providerLoadFailed": "Dostawcy nie mogli załadować", "newKnowledge.providerNotConfigured": "{{provider}} nie jest skonfigurowany", diff --git a/web/i18n/pt-BR/dataset.json b/web/i18n/pt-BR/dataset.json index 8bc68f5587c..52dacf0428c 100644 --- a/web/i18n/pt-BR/dataset.json +++ b/web/i18n/pt-BR/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Conecte o Notion para escolher páginas e mantê-las sincronizadas.", "newKnowledge.onlineDocuments": "Documentos on-line", "newKnowledge.onlineDrive": "Unidade on-line", + "newKnowledge.openDataSourceSettings": "Abrir configurações da fonte de dados", "newKnowledge.overview": "Visão geral", "newKnowledge.pagesAppearDescription": "Rastreie o site para revisar quais páginas serão adicionadas.", "newKnowledge.pagesAppearTitle": "As páginas aparecerão aqui", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Concluído", "newKnowledge.processingTaskState.superseded": "Substituído", "newKnowledge.providerConnected": "{{provider}} conectado", + "newKnowledge.providerCredentialRequiredDescription": "Configure uma credencial de {{provider}} em Fontes de dados e volte aqui para conectá-la.", "newKnowledge.providerLabel": "Selecione um provedor", "newKnowledge.providerLoadFailed": "Os provedores não conseguiram carregar", "newKnowledge.providerNotConfigured": "{{provider}} não está configurado", diff --git a/web/i18n/ro-RO/dataset.json b/web/i18n/ro-RO/dataset.json index 7aaeb7ef0d8..8a18c7704ab 100644 --- a/web/i18n/ro-RO/dataset.json +++ b/web/i18n/ro-RO/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Conectează Notion pentru a alege pagini și a le menține sincronizate.", "newKnowledge.onlineDocuments": "Documente online", "newKnowledge.onlineDrive": "Conducere online", + "newKnowledge.openDataSourceSettings": "Deschide setările surselor de date", "newKnowledge.overview": "Prezentare generală", "newKnowledge.pagesAppearDescription": "Explorează site-ul pentru a verifica paginile care vor fi adăugate.", "newKnowledge.pagesAppearTitle": "Paginile vor apărea aici", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Terminat", "newKnowledge.processingTaskState.superseded": "Inlocuit", "newKnowledge.providerConnected": "{{provider}} conectat", + "newKnowledge.providerCredentialRequiredDescription": "Configurează o acreditare {{provider}} în Surse de date, apoi revino aici pentru conectare.", "newKnowledge.providerLabel": "Selectați un furnizor", "newKnowledge.providerLoadFailed": "Furnizorii nu s-au putut încărca", "newKnowledge.providerNotConfigured": "{{provider}} nu este configurat", diff --git a/web/i18n/ru-RU/dataset.json b/web/i18n/ru-RU/dataset.json index 95527eb5d9e..9d3df8a63cc 100644 --- a/web/i18n/ru-RU/dataset.json +++ b/web/i18n/ru-RU/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Подключите Notion, чтобы выбирать страницы и поддерживать их синхронизацию.", "newKnowledge.onlineDocuments": "Онлайн-документы", "newKnowledge.onlineDrive": "Онлайн-диск", + "newKnowledge.openDataSourceSettings": "Открыть настройки источников данных", "newKnowledge.overview": "Обзор", "newKnowledge.pagesAppearDescription": "Просканируйте сайт, чтобы проверить, какие страницы будут добавлены.", "newKnowledge.pagesAppearTitle": "Здесь появятся страницы", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Завершено", "newKnowledge.processingTaskState.superseded": "Заменено", "newKnowledge.providerConnected": "{{provider}} подключен", + "newKnowledge.providerCredentialRequiredDescription": "Настройте учетные данные {{provider}} в источниках данных, затем вернитесь сюда для подключения.", "newKnowledge.providerLabel": "Выберите провайдера", "newKnowledge.providerLoadFailed": "Поставщикам не удалось загрузить", "newKnowledge.providerNotConfigured": "{{provider}} не настроено", diff --git a/web/i18n/sl-SI/dataset.json b/web/i18n/sl-SI/dataset.json index 5edb712bfa6..7f3297cc80b 100644 --- a/web/i18n/sl-SI/dataset.json +++ b/web/i18n/sl-SI/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Povežite Notion, da izberete strani in jih ohranite sinhronizirane.", "newKnowledge.onlineDocuments": "Spletni dokumenti", "newKnowledge.onlineDrive": "Spletna vožnja", + "newKnowledge.openDataSourceSettings": "Odpri nastavitve virov podatkov", "newKnowledge.overview": "Pregled", "newKnowledge.pagesAppearDescription": "Preiščite spletno mesto in preverite, katere strani bodo dodane.", "newKnowledge.pagesAppearTitle": "Strani bodo prikazane tukaj", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Dokončano", "newKnowledge.processingTaskState.superseded": "Nadomeščeno", "newKnowledge.providerConnected": "{{provider}} je povezan", + "newKnowledge.providerCredentialRequiredDescription": "V virih podatkov nastavite poverilnico za {{provider}}, nato se vrnite sem in vzpostavite povezavo.", "newKnowledge.providerLabel": "Izberi ponudnika", "newKnowledge.providerLoadFailed": "Ponudnikov ni bilo mogoče naložiti", "newKnowledge.providerNotConfigured": "{{provider}} ni konfiguriran", diff --git a/web/i18n/th-TH/dataset.json b/web/i18n/th-TH/dataset.json index d040287fcfb..bd88a8075fe 100644 --- a/web/i18n/th-TH/dataset.json +++ b/web/i18n/th-TH/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "เชื่อมต่อ Notion เพื่อเลือกหน้าและคงการซิงค์ไว้", "newKnowledge.onlineDocuments": "เอกสารออนไลน์", "newKnowledge.onlineDrive": "ไดรฟ์ออนไลน์", + "newKnowledge.openDataSourceSettings": "เปิดการตั้งค่าแหล่งข้อมูล", "newKnowledge.overview": "ภาพรวม", "newKnowledge.pagesAppearDescription": "รวบรวมข้อมูลเว็บไซต์เพื่อตรวจสอบหน้าที่จะเพิ่ม", "newKnowledge.pagesAppearTitle": "หน้าจะแสดงที่นี่", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "เสร็จสิ้น", "newKnowledge.processingTaskState.superseded": "เข้ามาแทนที่", "newKnowledge.providerConnected": "เชื่อมต่อ {{provider}} แล้ว", + "newKnowledge.providerCredentialRequiredDescription": "กำหนดค่าข้อมูลรับรอง {{provider}} ในแหล่งข้อมูล แล้วกลับมาที่นี่เพื่อเชื่อมต่อ", "newKnowledge.providerLabel": "เลือกผู้ให้บริการ", "newKnowledge.providerLoadFailed": "ผู้ให้บริการไม่สามารถโหลดได้", "newKnowledge.providerNotConfigured": "{{provider}} ไม่ได้กำหนดค่า", diff --git a/web/i18n/tr-TR/dataset.json b/web/i18n/tr-TR/dataset.json index 4d58a33ef82..d13e881131a 100644 --- a/web/i18n/tr-TR/dataset.json +++ b/web/i18n/tr-TR/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Sayfaları seçmek ve eşitlenmiş tutmak için Notion’a bağlanın.", "newKnowledge.onlineDocuments": "Çevrimiçi belgeler", "newKnowledge.onlineDrive": "Çevrimiçi sürücü", + "newKnowledge.openDataSourceSettings": "Veri Kaynağı ayarlarını aç", "newKnowledge.overview": "Genel bakış", "newKnowledge.pagesAppearDescription": "Hangi sayfaların ekleneceğini görmek için siteyi tarayın.", "newKnowledge.pagesAppearTitle": "Sayfalar burada görünecek", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Tamamlandı", "newKnowledge.processingTaskState.superseded": "Değiştirildi", "newKnowledge.providerConnected": "{{provider}} bağlandı", + "newKnowledge.providerCredentialRequiredDescription": "Veri Kaynakları'nda bir {{provider}} kimlik bilgisi yapılandırın, ardından bağlanmak için buraya dönün.", "newKnowledge.providerLabel": "Bir sağlayıcı seçin", "newKnowledge.providerLoadFailed": "Sağlayıcılar yüklenemedi", "newKnowledge.providerNotConfigured": "{{provider}} yapılandırılmadı", diff --git a/web/i18n/uk-UA/dataset.json b/web/i18n/uk-UA/dataset.json index 18979829a2f..3f8c1304d18 100644 --- a/web/i18n/uk-UA/dataset.json +++ b/web/i18n/uk-UA/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Підключіть Notion, щоб вибирати сторінки й підтримувати їх синхронізацію.", "newKnowledge.onlineDocuments": "Документи онлайн", "newKnowledge.onlineDrive": "Онлайн драйв", + "newKnowledge.openDataSourceSettings": "Відкрити налаштування джерел даних", "newKnowledge.overview": "Огляд", "newKnowledge.pagesAppearDescription": "Проскануйте сайт, щоб переглянути сторінки, які буде додано.", "newKnowledge.pagesAppearTitle": "Тут з’являться сторінки", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Виконано", "newKnowledge.processingTaskState.superseded": "Замінено", "newKnowledge.providerConnected": "{{provider}} підключено", + "newKnowledge.providerCredentialRequiredDescription": "Налаштуйте облікові дані {{provider}} у джерелах даних, а потім поверніться сюди для підключення.", "newKnowledge.providerLabel": "Оберіть провайдера", "newKnowledge.providerLoadFailed": "Не вдалося завантажити постачальників", "newKnowledge.providerNotConfigured": "{{provider}} не налаштовано", diff --git a/web/i18n/vi-VN/dataset.json b/web/i18n/vi-VN/dataset.json index f24db177e36..8b4e7cd3425 100644 --- a/web/i18n/vi-VN/dataset.json +++ b/web/i18n/vi-VN/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "Kết nối Notion để chọn trang và duy trì đồng bộ.", "newKnowledge.onlineDocuments": "Tài liệu trực tuyến", "newKnowledge.onlineDrive": "Ổ đĩa trực tuyến", + "newKnowledge.openDataSourceSettings": "Mở cài đặt Nguồn dữ liệu", "newKnowledge.overview": "Tổng quan", "newKnowledge.pagesAppearDescription": "Thu thập trang web để xem những trang nào sẽ được thêm.", "newKnowledge.pagesAppearTitle": "Các trang sẽ xuất hiện ở đây", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "Đã hoàn thành", "newKnowledge.processingTaskState.superseded": "Đã thay thế", "newKnowledge.providerConnected": "Đã kết nối {{provider}}", + "newKnowledge.providerCredentialRequiredDescription": "Định cấu hình thông tin xác thực {{provider}} trong Nguồn dữ liệu, sau đó quay lại đây để kết nối.", "newKnowledge.providerLabel": "Chọn nhà cung cấp", "newKnowledge.providerLoadFailed": "Nhà cung cấp không thể tải", "newKnowledge.providerNotConfigured": "{{provider}} chưa được định cấu hình", diff --git a/web/i18n/zh-Hans/dataset.json b/web/i18n/zh-Hans/dataset.json index 5d33cabf359..2ed916a4c63 100644 --- a/web/i18n/zh-Hans/dataset.json +++ b/web/i18n/zh-Hans/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "连接 Notion 以选择页面并保持同步。", "newKnowledge.onlineDocuments": "在线文档", "newKnowledge.onlineDrive": "在线驱动器", + "newKnowledge.openDataSourceSettings": "打开数据来源设置", "newKnowledge.overview": "概览", "newKnowledge.pagesAppearDescription": "抓取网站以查看将添加哪些页面。", "newKnowledge.pagesAppearTitle": "页面将显示在此处", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "已完成", "newKnowledge.processingTaskState.superseded": "已被取代", "newKnowledge.providerConnected": "{{provider}}已连接", + "newKnowledge.providerCredentialRequiredDescription": "请先在数据来源中配置 {{provider}} 凭据,然后返回此处连接。", "newKnowledge.providerLabel": "提供商", "newKnowledge.providerLoadFailed": "提供商无法加载", "newKnowledge.providerNotConfigured": "{{provider}} 未配置", diff --git a/web/i18n/zh-Hant/dataset.json b/web/i18n/zh-Hant/dataset.json index 06c5b72d905..7ea2b1c53e9 100644 --- a/web/i18n/zh-Hant/dataset.json +++ b/web/i18n/zh-Hant/dataset.json @@ -318,6 +318,7 @@ "newKnowledge.notionNotConnectedDescription": "連接 Notion 以選擇頁面並保持同步。", "newKnowledge.onlineDocuments": "線上文檔", "newKnowledge.onlineDrive": "線上驅動器", + "newKnowledge.openDataSourceSettings": "開啟資料來源設定", "newKnowledge.overview": "概覽", "newKnowledge.pagesAppearDescription": "抓取網站以查看將新增哪些頁面。", "newKnowledge.pagesAppearTitle": "頁面將顯示在此處", @@ -344,6 +345,7 @@ "newKnowledge.processingTaskState.succeeded": "已完成", "newKnowledge.processingTaskState.superseded": "已被取代", "newKnowledge.providerConnected": "{{provider}}已連接", + "newKnowledge.providerCredentialRequiredDescription": "請先在資料來源中設定 {{provider}} 憑證,然後返回此處進行連線。", "newKnowledge.providerLabel": "供應商", "newKnowledge.providerLoadFailed": "提供者無法載入", "newKnowledge.providerNotConfigured": "{{provider}} 未配置", diff --git a/web/service/client.spec.ts b/web/service/client.spec.ts index 51bb42a4eb9..18b0aba79cb 100644 --- a/web/service/client.spec.ts +++ b/web/service/client.spec.ts @@ -1,6 +1,5 @@ import type { ApiBasedExtensionResponse } from '@dify/contracts/api/console/api-based-extension/types.gen' import type { TagResponse as Tag } from '@dify/contracts/api/console/tags/types.gen' -import type { DocumentProcessingTaskEvent } from '@dify/contracts/knowledge-fs/types.gen' import type { MutationFunctionContext, QueryFunctionContext } from '@tanstack/react-query' import type { consoleQuery as ConsoleQuery } from './client' import { QueryClient } from '@tanstack/react-query' @@ -404,71 +403,33 @@ describe('consoleQuery transport context', () => { expect(requestURL.searchParams.has('ids[1]')).toBe(false) }) - it('should consume KnowledgeFS processing events through the generated stream contract', async () => { + it('should request KnowledgeFS documents through the control-space contract', async () => { const request = vi.fn().mockResolvedValue( - new Response( - [ - 'id: task-1:1', - 'event: message', - 'data: {"event":"progress","data":{"progressPercent":25,"stage":"parsed","state":"running","updatedAt":"2026-07-22T10:00:00.000Z"}}', - '', - 'id: task-1:terminal', - 'event: message', - 'data: {"event":"terminal","data":{"state":"succeeded"}}', - '', - '', - ].join('\n'), - { - status: 200, - headers: { - 'content-type': 'text/event-stream', - }, - }, - ), + new Response(JSON.stringify({ data: [], next_cursor: null }), { + status: 200, + headers: { 'content-type': 'application/json' }, + }), ) const consoleQuery = await loadConsoleQueryWithRequest(request) const queryOptions = - consoleQuery.knowledgeFs.getKnowledgeSpacesByIdDocumentsByDocumentIdProcessingTasksByTaskIdEvents.experimental_streamedOptions( - { - input: { - headers: { - 'last-event-id': 'task-1:0', - }, - params: { - documentId: 'document-1', - id: 'space-1', - taskId: 'task-1', - }, + consoleQuery.knowledgeFs.spaces.byControlSpaceId.documents.get.queryOptions({ + input: { + params: { + control_space_id: 'space-1', }, + query: { cursor: 'cursor-1' }, }, - ) + }) - const events = await queryOptions.queryFn({ - client: new QueryClient(), + const result = await queryOptions.queryFn({ signal: new AbortController().signal, } as QueryFunctionContext) - expectTypeOf(events[0]!).toMatchTypeOf() - expect(events).toEqual([ - { - data: { - progressPercent: 25, - stage: 'parsed', - state: 'running', - updatedAt: '2026-07-22T10:00:00.000Z', - }, - event: 'progress', - }, - { data: { state: 'succeeded' }, event: 'terminal' }, - ]) + expect(result).toEqual({ data: [], next_cursor: null }) expect(request).toHaveBeenCalledWith( - expect.stringContaining( - '/knowledge-fs/knowledge-spaces/space-1/documents/document-1/processing-tasks/task-1/events', - ), + expect.stringContaining('/knowledge-fs/spaces/space-1/documents?cursor=cursor-1'), expect.any(Object), - expect.objectContaining({ - fetchCompat: true, - }), + expect.objectContaining({ fetchCompat: true }), ) }) }) diff --git a/web/service/console-router-loader.ts b/web/service/console-router-loader.ts index d4a5891d3c1..58989f3e7d9 100644 --- a/web/service/console-router-loader.ts +++ b/web/service/console-router-loader.ts @@ -16,14 +16,8 @@ async function loadEnterpriseContract(): Promise { return { enterprise: contract } } -async function loadKnowledgeFsContract(): Promise { - const { contract } = await import('@dify/contracts/knowledge-fs/orpc.gen') - return { knowledgeFs: contract } -} - export async function loadConsoleContractForSegment(segment: string) { if (segment === 'enterprise') return loadEnterpriseContract() - if (segment === 'knowledgeFs') return loadKnowledgeFsContract() const generatedContract = await loadGeneratedConsoleContract(segment) if (generatedContract) return generatedContract diff --git a/web/test/console/system-features.ts b/web/test/console/system-features.ts index 761a0213927..f568fd3eb8f 100644 --- a/web/test/console/system-features.ts +++ b/web/test/console/system-features.ts @@ -60,6 +60,7 @@ const baseSystemFeatures = { enable_learn_app: true, enable_step_by_step_tour: false, knowledge_fs_enabled: false, + knowledge_fs_upload_enabled: false, } satisfies GetSystemFeaturesResponse const baseSystemFeaturesLicense = {