304 lines
9.2 KiB
Python
304 lines
9.2 KiB
Python
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",
|
|
]
|