from __future__ import annotations from datetime import datetime from typing import Any, Literal from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator KnowledgeMaturity = Literal[ "discover", "link", "search", "read", "publish", "synchronize", "migrate", ] KnowledgeVisibility = Literal["tenant", "restricted"] class KnowledgeNamespaceMapping(BaseModel): model_config = ConfigDict(extra="forbid") source_namespace_id: int source_name: str = Field(default="", max_length=200) target_space_ref: str = Field(min_length=1, max_length=255) target_path_prefix: str = Field(default="", max_length=500) include: bool = True visibility: KnowledgeVisibility | None = None acl_tokens: list[str] = Field(default_factory=list, max_length=500) @field_validator("acl_tokens") @classmethod def normalize_acl_tokens(cls, values: list[str]) -> list[str]: return _normalized_tokens(values) @model_validator(mode="after") def restricted_mapping_requires_acl(self) -> "KnowledgeNamespaceMapping": if self.visibility == "restricted" and not self.acl_tokens: raise ValueError("Restricted namespace mappings require ACL tokens") return self class KnowledgeProfileCreateRequest(BaseModel): model_config = ConfigDict(extra="forbid") configuration_id: str = Field(min_length=1, max_length=36) desired_maturity: KnowledgeMaturity = "read" source_authority_mode: Literal[ "external_authoritative", "external_mirror", "governed_sync", "linked_reference", ] = "external_mirror" default_visibility: KnowledgeVisibility = "restricted" default_acl_tokens: list[str] = Field(default_factory=list, max_length=500) namespace_mappings: list[KnowledgeNamespaceMapping] = Field( min_length=1, max_length=500 ) @field_validator("default_acl_tokens") @classmethod def normalize_acl_tokens(cls, values: list[str]) -> list[str]: return _normalized_tokens(values) @model_validator(mode="after") def restricted_profile_requires_acl(self) -> "KnowledgeProfileCreateRequest": if self.default_visibility == "restricted" and not self.default_acl_tokens: raise ValueError("Restricted knowledge profiles require default ACL tokens") return self class KnowledgeProfileUpdateRequest(BaseModel): model_config = ConfigDict(extra="forbid") expected_resource_revision: int = Field(ge=1) status: Literal["active", "paused"] | None = None desired_maturity: KnowledgeMaturity | None = None source_authority_mode: Literal[ "external_authoritative", "external_mirror", "governed_sync", "linked_reference", ] | None = None default_visibility: KnowledgeVisibility | None = None default_acl_tokens: list[str] | None = Field(default=None, max_length=500) namespace_mappings: list[KnowledgeNamespaceMapping] | None = Field( default=None, min_length=1, max_length=500 ) @field_validator("default_acl_tokens") @classmethod def normalize_acl_tokens(cls, values: list[str] | None) -> list[str] | None: return _normalized_tokens(values) if values is not None else None class KnowledgeProfileItem(BaseModel): id: str tenant_id: str configuration_id: str status: str product: str product_version: str | None = None desired_maturity: str discovered_maturity: str source_authority_mode: str default_visibility: str default_acl_tokens: list[str] namespace_mappings: list[dict[str, Any]] capabilities: list[str] discovery_revision: str | None = None health_status: str health_details: dict[str, Any] discovered_at: datetime | None = None last_sync_cursor: str | None = None last_high_watermark: str | None = None resource_revision: int credential_reference_present: bool = False endpoint_configured: bool = False created_at: datetime updated_at: datetime class KnowledgeProfileListResponse(BaseModel): items: list[KnowledgeProfileItem] class KnowledgeDiagnostic(BaseModel): severity: Literal["info", "warning", "error"] code: str message: str object_ref: str | None = None field: str | None = None retryable: bool = False details: dict[str, Any] = Field(default_factory=dict) class KnowledgeDiscoveryResponse(BaseModel): profile: KnowledgeProfileItem product: str product_version: str | None = None api_version: str | None = None capabilities: list[str] namespaces: list[dict[str, Any]] extensions: list[dict[str, Any]] maturity: str health_status: str diagnostics: list[KnowledgeDiagnostic] revision: str class KnowledgeSyncRequest(BaseModel): model_config = ConfigDict(extra="forbid") idempotency_key: str = Field(min_length=1, max_length=255) cursor: str | None = Field(default=None, max_length=500) force_full: bool = False limit: int = Field(default=100, ge=1, le=500) class KnowledgeSyncRunItem(BaseModel): id: str tenant_id: str profile_id: str mode: str idempotency_key: str status: str cursor_before: str | None = None cursor_after: str | None = None high_watermark: str | None = None counts: dict[str, Any] effects: list[dict[str, Any]] diagnostics: list[KnowledgeDiagnostic] provenance: dict[str, Any] started_at: datetime finished_at: datetime | None = None created_at: datetime class KnowledgeSyncRunListResponse(BaseModel): items: list[KnowledgeSyncRunItem] class KnowledgeExternalReferenceResponse(BaseModel): system: str object_type: str object_id: str maturity: str authority_mode: str connector_id: str | None = None canonical_url: str | None = None version: str | None = None etag: str | None = None observed_at: str | None = None metadata: dict[str, Any] class KnowledgeObjectItem(BaseModel): id: str profile_id: str object_type: str external_id: str external_page_id: str | None = None external_revision_id: str | None = None namespace_id: int | None = None title: str canonical_url: str | None = None status: str redirect_target_external_id: str | None = None source_revision: str visibility: str acl_tokens: list[str] mapped_data: dict[str, Any] external_reference: KnowledgeExternalReferenceResponse source_updated_at: datetime | None = None observed_at: datetime resource_revision: int class KnowledgeObjectListResponse(BaseModel): items: list[KnowledgeObjectItem] next_cursor: str | None = None class KnowledgeMigrationTargetState(BaseModel): model_config = ConfigDict(extra="forbid") path: str = Field(min_length=1, max_length=500) source_external_id: str | None = Field(default=None, max_length=255) attachment_names: list[str] = Field(default_factory=list, max_length=500) class KnowledgeMigrationDryRunRequest(BaseModel): model_config = ConfigDict(extra="forbid") idempotency_key: str = Field(min_length=1, max_length=255) target_space_ref: str = Field(min_length=1, max_length=255) max_items: int = Field(default=100, ge=1, le=500) supported_macros: list[str] = Field(default_factory=list, max_length=200) existing_targets: list[KnowledgeMigrationTargetState] = Field( default_factory=list, max_length=5_000 ) class KnowledgeMigrationDryRunResponse(BaseModel): run: KnowledgeSyncRunItem target_space_ref: str source_revision: str source_fingerprint: str summary: dict[str, int] effects: list[dict[str, Any]] diagnostics: list[KnowledgeDiagnostic] truncated: bool can_apply: bool class KnowledgePublishRequest(BaseModel): model_config = ConfigDict(extra="forbid") idempotency_key: str = Field(min_length=1, max_length=255) title: str = Field(min_length=1, max_length=500) body: str = Field(max_length=200_000) summary: str = Field(default="", max_length=500) expected_external_revision: str | None = Field(default=None, max_length=255) minor: bool = False class KnowledgePublishResponse(BaseModel): run: KnowledgeSyncRunItem external_reference: KnowledgeExternalReferenceResponse accepted: bool outcome_unknown: bool = False def _normalized_tokens(values: list[str]) -> list[str]: normalized = [str(item).strip() for item in values] if any(not item or len(item) > 500 for item in normalized): raise ValueError("ACL tokens must contain 1 to 500 characters") if len(normalized) != len(set(normalized)): raise ValueError("ACL tokens must be unique") return normalized __all__ = [ "KnowledgeDiagnostic", "KnowledgeDiscoveryResponse", "KnowledgeExternalReferenceResponse", "KnowledgeMigrationDryRunRequest", "KnowledgeMigrationDryRunResponse", "KnowledgeMigrationTargetState", "KnowledgeNamespaceMapping", "KnowledgeObjectItem", "KnowledgeObjectListResponse", "KnowledgeProfileCreateRequest", "KnowledgeProfileItem", "KnowledgeProfileListResponse", "KnowledgeProfileUpdateRequest", "KnowledgePublishRequest", "KnowledgePublishResponse", "KnowledgeSyncRequest", "KnowledgeSyncRunItem", "KnowledgeSyncRunListResponse", ]