225 lines
6.7 KiB
Python
225 lines
6.7 KiB
Python
from collections.abc import Mapping
|
|
|
|
from pydantic import Field
|
|
from pydantic import field_validator
|
|
|
|
from .common import DocsPath
|
|
from .common import StrictFrozenModel
|
|
from .common import frozen_mapping
|
|
from .prompt import PromptArgumentEntry
|
|
|
|
|
|
def _empty_docs_mapping() -> Mapping[DocsPath, str]:
|
|
return frozen_mapping()
|
|
|
|
|
|
class ReferenceRecord(StrictFrozenModel):
|
|
"""Registry record for a resolved skill reference document."""
|
|
|
|
ref_id: str
|
|
uri: str
|
|
relpath: DocsPath
|
|
mime_type: str
|
|
title: str | None
|
|
content: str
|
|
|
|
|
|
class SkillRecord(StrictFrozenModel):
|
|
"""Registry record containing a fully resolved skill and references."""
|
|
|
|
skill_id: str
|
|
name: str
|
|
description: str
|
|
version: str
|
|
tags: tuple[str, ...]
|
|
capabilities: tuple[str, ...]
|
|
document_uri: str
|
|
document_relpath: DocsPath
|
|
document_content: str
|
|
references: Mapping[str, ReferenceRecord] = Field(default_factory=frozen_mapping)
|
|
|
|
@field_validator("references", mode="before")
|
|
@classmethod
|
|
def freeze_references(cls, value: Mapping[str, ReferenceRecord] | None) -> Mapping[str, ReferenceRecord]:
|
|
return frozen_mapping(value)
|
|
|
|
|
|
class SkillSummaryRecord(StrictFrozenModel):
|
|
"""Compact skill summary exposed by catalog listing APIs."""
|
|
|
|
skill_id: str
|
|
name: str
|
|
description: str
|
|
tags: tuple[str, ...]
|
|
capabilities: tuple[str, ...]
|
|
document_uri: str
|
|
version: str
|
|
|
|
@classmethod
|
|
def from_record(cls, record: SkillRecord) -> "SkillSummaryRecord":
|
|
return cls(
|
|
skill_id=record.skill_id,
|
|
name=record.name,
|
|
description=record.description,
|
|
tags=record.tags,
|
|
capabilities=record.capabilities,
|
|
document_uri=record.document_uri,
|
|
version=record.version,
|
|
)
|
|
|
|
|
|
class PromptRecord(StrictFrozenModel):
|
|
"""Registry record containing a fully resolved prompt document."""
|
|
|
|
prompt_id: str
|
|
name: str
|
|
description: str
|
|
version: str
|
|
tags: tuple[str, ...]
|
|
capabilities: tuple[str, ...]
|
|
arguments: Mapping[str, PromptArgumentEntry] = Field(default_factory=frozen_mapping)
|
|
document_uri: str
|
|
document_relpath: DocsPath
|
|
document_content: str
|
|
|
|
@field_validator("arguments", mode="before")
|
|
@classmethod
|
|
def freeze_arguments(cls, value: Mapping[str, PromptArgumentEntry] | None) -> Mapping[str, PromptArgumentEntry]:
|
|
return frozen_mapping(value)
|
|
|
|
|
|
class PromptSummaryRecord(StrictFrozenModel):
|
|
"""Compact prompt summary exposed by catalog listing APIs."""
|
|
|
|
prompt_id: str
|
|
name: str
|
|
description: str
|
|
tags: tuple[str, ...]
|
|
capabilities: tuple[str, ...]
|
|
document_uri: str
|
|
version: str
|
|
|
|
@classmethod
|
|
def from_record(cls, record: PromptRecord) -> "PromptSummaryRecord":
|
|
return cls(
|
|
prompt_id=record.prompt_id,
|
|
name=record.name,
|
|
description=record.description,
|
|
tags=record.tags,
|
|
capabilities=record.capabilities,
|
|
document_uri=record.document_uri,
|
|
version=record.version,
|
|
)
|
|
|
|
|
|
class SkillPatternPayload(StrictFrozenModel):
|
|
"""Catalog payload model for skill pattern search results."""
|
|
|
|
id: str
|
|
name: str
|
|
version: str
|
|
description: str
|
|
tags: list[str]
|
|
capabilities: list[str]
|
|
resources: list[str]
|
|
|
|
@classmethod
|
|
def from_record(cls, record: SkillRecord) -> "SkillPatternPayload":
|
|
return cls(
|
|
id=record.skill_id,
|
|
name=record.name,
|
|
version=record.version,
|
|
description=record.description,
|
|
tags=list(record.tags),
|
|
capabilities=list(record.capabilities),
|
|
resources=list(record.capabilities),
|
|
)
|
|
|
|
|
|
class SkillSummaryPayload(StrictFrozenModel):
|
|
"""Catalog payload model for skill index summaries."""
|
|
|
|
id: str
|
|
name: str
|
|
description: str
|
|
tags: list[str]
|
|
capabilities: list[str]
|
|
version: str
|
|
document_uri: str
|
|
detail_uri: str
|
|
resources: dict[str, str | list[str]]
|
|
|
|
@classmethod
|
|
def from_record(cls, record: SkillRecord) -> "SkillSummaryPayload":
|
|
return cls(
|
|
id=record.skill_id,
|
|
name=record.name,
|
|
description=record.description,
|
|
tags=list(record.tags),
|
|
capabilities=list(record.capabilities),
|
|
version=record.version,
|
|
document_uri=record.document_uri,
|
|
detail_uri=f"resource://catalog/skills/{record.skill_id}",
|
|
resources={
|
|
"document": record.document_uri,
|
|
"references": [
|
|
f"resource://skills/{record.skill_id}/references/{ref_id}" for ref_id in sorted(record.references)
|
|
],
|
|
},
|
|
)
|
|
|
|
|
|
class PromptSummaryPayload(StrictFrozenModel):
|
|
"""Catalog payload model for prompt index summaries."""
|
|
|
|
id: str
|
|
name: str
|
|
description: str
|
|
tags: list[str]
|
|
capabilities: list[str]
|
|
version: str
|
|
document_uri: str
|
|
detail_uri: str
|
|
|
|
@classmethod
|
|
def from_record(cls, record: PromptRecord) -> "PromptSummaryPayload":
|
|
return cls(
|
|
id=record.prompt_id,
|
|
name=record.name,
|
|
description=record.description,
|
|
tags=list(record.tags),
|
|
capabilities=list(record.capabilities),
|
|
version=record.version,
|
|
document_uri=record.document_uri,
|
|
detail_uri=f"resource://catalog/prompts/{record.prompt_id}",
|
|
)
|
|
|
|
|
|
class DocsRegistry(StrictFrozenModel):
|
|
"""In-memory index of loaded skills, prompts, and docs content."""
|
|
|
|
skills_by_id: Mapping[str, SkillRecord] = Field(default_factory=frozen_mapping)
|
|
skills_in_load_order: tuple[str, ...]
|
|
skills_summary_in_load_order: tuple[SkillSummaryRecord, ...]
|
|
docs_markdown_by_path: Mapping[DocsPath, str] = Field(default_factory=_empty_docs_mapping)
|
|
docs_markdown_path_index: tuple[DocsPath, ...]
|
|
tag_to_skill_ids: Mapping[str, tuple[str, ...]] = Field(default_factory=frozen_mapping)
|
|
capability_to_skill_ids: Mapping[str, tuple[str, ...]] = Field(default_factory=frozen_mapping)
|
|
prompts_by_id: Mapping[str, PromptRecord] = Field(default_factory=frozen_mapping)
|
|
prompts_in_load_order: tuple[str, ...] = ()
|
|
prompts_summary_in_load_order: tuple[PromptSummaryRecord, ...] = ()
|
|
tag_to_prompt_ids: Mapping[str, tuple[str, ...]] = Field(default_factory=frozen_mapping)
|
|
|
|
@field_validator(
|
|
"skills_by_id",
|
|
"docs_markdown_by_path",
|
|
"tag_to_skill_ids",
|
|
"capability_to_skill_ids",
|
|
"prompts_by_id",
|
|
"tag_to_prompt_ids",
|
|
mode="before",
|
|
)
|
|
@classmethod
|
|
def freeze_mappings(cls, value: Mapping[str, object] | None) -> Mapping[str, object]:
|
|
return frozen_mapping(value)
|