typing
This commit is contained in:
@@ -2,21 +2,19 @@ import re
|
|||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from pathlib import PurePosixPath
|
from pathlib import PurePosixPath
|
||||||
from types import MappingProxyType
|
from types import MappingProxyType
|
||||||
from typing import TypeVar
|
from typing import ClassVar
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from pydantic import ConfigDict
|
from pydantic import ConfigDict
|
||||||
from pydantic import field_validator
|
from pydantic import field_validator
|
||||||
|
|
||||||
SKILL_ID_RE = re.compile(r"^[a-z][a-z0-9-]*$")
|
SKILL_ID_RE: Final[re.Pattern[str]] = re.compile(r"^[a-z][a-z0-9-]*$")
|
||||||
SEMVER_RE = re.compile(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:[-+][0-9A-Za-z.-]+)?$")
|
SEMVER_RE: Final[re.Pattern[str]] = re.compile(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:[-+][0-9A-Za-z.-]+)?$")
|
||||||
|
|
||||||
K = TypeVar("K")
|
|
||||||
V = TypeVar("V")
|
|
||||||
|
|
||||||
|
|
||||||
class StrictFrozenModel(BaseModel):
|
class StrictFrozenModel(BaseModel):
|
||||||
model_config = ConfigDict(
|
model_config: ClassVar[ConfigDict] = ConfigDict(
|
||||||
extra="forbid",
|
extra="forbid",
|
||||||
frozen=True,
|
frozen=True,
|
||||||
validate_by_alias=True,
|
validate_by_alias=True,
|
||||||
@@ -25,8 +23,8 @@ class StrictFrozenModel(BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def frozen_mapping(value: Mapping[K, V] | None = None) -> Mapping[K, V]:
|
def frozen_mapping[K, V](value: Mapping[K, V] | None = None) -> Mapping[K, V]:
|
||||||
return MappingProxyType(dict(value or {}))
|
return MappingProxyType(dict(value) if value is not None else {})
|
||||||
|
|
||||||
|
|
||||||
class ReferenceEntry(StrictFrozenModel):
|
class ReferenceEntry(StrictFrozenModel):
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from pathlib import PurePosixPath
|
from pathlib import PurePosixPath
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
from .common import StrictFrozenModel
|
from .common import StrictFrozenModel
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ class SkillFilesBundleModel(StrictFrozenModel):
|
|||||||
skill: MarkdownDocumentModel,
|
skill: MarkdownDocumentModel,
|
||||||
references: tuple[MarkdownDocumentModel, ...] = (),
|
references: tuple[MarkdownDocumentModel, ...] = (),
|
||||||
other: tuple[MarkdownDocumentModel, ...] = (),
|
other: tuple[MarkdownDocumentModel, ...] = (),
|
||||||
) -> "SkillFilesBundleModel":
|
) -> Self:
|
||||||
# Ensure deterministic ordering even when the source collection is unordered.
|
# Ensure deterministic ordering even when the source collection is unordered.
|
||||||
references_sorted = tuple(sorted(references, key=lambda doc: doc.relpath))
|
references_sorted = tuple(sorted(references, key=lambda doc: doc.relpath))
|
||||||
other_sorted = tuple(sorted(other, key=lambda doc: doc.relpath))
|
other_sorted = tuple(sorted(other, key=lambda doc: doc.relpath))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
from pydantic import field_validator
|
from pydantic import field_validator
|
||||||
@@ -10,29 +11,23 @@ from .common import SKILL_ID_RE
|
|||||||
from .common import StrictFrozenModel
|
from .common import StrictFrozenModel
|
||||||
from .common import frozen_mapping
|
from .common import frozen_mapping
|
||||||
|
|
||||||
|
type PromptArgumentType = Literal[
|
||||||
|
"string",
|
||||||
|
"number",
|
||||||
|
"integer",
|
||||||
|
"boolean",
|
||||||
|
"array",
|
||||||
|
"object",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class PromptArgumentEntry(StrictFrozenModel):
|
class PromptArgumentEntry(StrictFrozenModel):
|
||||||
type: str = Field(min_length=1)
|
type: PromptArgumentType
|
||||||
description: str | None = None
|
description: str | None = None
|
||||||
required: bool = False
|
required: bool = False
|
||||||
default: Any | None = None
|
default: Any | None = None
|
||||||
enum: tuple[str, ...] | None = None
|
enum: tuple[str, ...] | None = None
|
||||||
|
|
||||||
@field_validator("type")
|
|
||||||
@classmethod
|
|
||||||
def validate_type(cls, value: str) -> str:
|
|
||||||
allowed_types = {
|
|
||||||
"string",
|
|
||||||
"number",
|
|
||||||
"integer",
|
|
||||||
"boolean",
|
|
||||||
"array",
|
|
||||||
"object",
|
|
||||||
}
|
|
||||||
if value not in allowed_types:
|
|
||||||
raise ValueError(f"unsupported prompt argument type: {value}")
|
|
||||||
return value
|
|
||||||
|
|
||||||
@field_validator("enum")
|
@field_validator("enum")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_enum(cls, value: tuple[str, ...] | None) -> tuple[str, ...] | None:
|
def validate_enum(cls, value: tuple[str, ...] | None) -> tuple[str, ...] | None:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from .common import ReferenceEntry
|
|||||||
from .common import StrictFrozenModel
|
from .common import StrictFrozenModel
|
||||||
from .common import frozen_mapping
|
from .common import frozen_mapping
|
||||||
|
|
||||||
|
type ToolSelector = str | tuple[str, ...]
|
||||||
|
|
||||||
|
|
||||||
class SkillMetadata(StrictFrozenModel):
|
class SkillMetadata(StrictFrozenModel):
|
||||||
id: str
|
id: str
|
||||||
@@ -66,8 +68,8 @@ class SkillFrontmatter(StrictFrozenModel):
|
|||||||
name: str = Field(min_length=1, max_length=64)
|
name: str = Field(min_length=1, max_length=64)
|
||||||
description: str = Field(min_length=1, max_length=1024)
|
description: str = Field(min_length=1, max_length=1024)
|
||||||
when_to_use: str | None = None
|
when_to_use: str | None = None
|
||||||
allowed_tools: str | tuple[str, ...] | None = Field(default=None, alias="allowed-tools")
|
allowed_tools: ToolSelector | None = Field(default=None, alias="allowed-tools")
|
||||||
disallowed_tools: str | tuple[str, ...] | None = Field(
|
disallowed_tools: ToolSelector | None = Field(
|
||||||
default=None,
|
default=None,
|
||||||
alias="disallowed-tools",
|
alias="disallowed-tools",
|
||||||
)
|
)
|
||||||
@@ -77,7 +79,7 @@ class SkillFrontmatter(StrictFrozenModel):
|
|||||||
)
|
)
|
||||||
user_invocable: bool | None = Field(default=None, alias="user-invocable")
|
user_invocable: bool | None = Field(default=None, alias="user-invocable")
|
||||||
argument_hint: str | None = Field(default=None, alias="argument-hint")
|
argument_hint: str | None = Field(default=None, alias="argument-hint")
|
||||||
arguments: str | tuple[str, ...] | None = None
|
arguments: ToolSelector | None = None
|
||||||
license: str | None = None
|
license: str | None = None
|
||||||
compatibility: str | None = None
|
compatibility: str | None = None
|
||||||
metadata: Mapping[str, str] | None = None
|
metadata: Mapping[str, str] | None = None
|
||||||
|
|||||||
Reference in New Issue
Block a user