started prompt mechanics
This commit is contained in:
@@ -1,13 +1,21 @@
|
||||
from personal_mcp.catalog.server import (
|
||||
build_prompt_detail_payload,
|
||||
build_prompts_index_payload,
|
||||
build_skill_detail_payload,
|
||||
build_skills_index_payload,
|
||||
get_pattern_by_id_payload,
|
||||
get_prompt_by_id_payload,
|
||||
search_patterns_payload,
|
||||
search_prompts_payload,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"build_skill_detail_payload",
|
||||
"build_prompt_detail_payload",
|
||||
"build_prompts_index_payload",
|
||||
"build_skills_index_payload",
|
||||
"get_prompt_by_id_payload",
|
||||
"get_pattern_by_id_payload",
|
||||
"search_prompts_payload",
|
||||
"search_patterns_payload",
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from personal_mcp.skills.document_loader import DocsRegistry, SkillRecord
|
||||
from personal_mcp.skills.document_loader import DocsRegistry, PromptRecord, SkillRecord
|
||||
|
||||
DEFAULT_LIMIT = 20
|
||||
MAX_LIMIT = 100
|
||||
@@ -41,6 +41,19 @@ def _summary_payload(skill: SkillRecord) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _prompt_summary_payload(prompt: PromptRecord) -> dict[str, Any]:
|
||||
return {
|
||||
"id": prompt.prompt_id,
|
||||
"name": prompt.name,
|
||||
"description": prompt.description,
|
||||
"tags": list(prompt.tags),
|
||||
"capabilities": list(prompt.capabilities),
|
||||
"version": prompt.version,
|
||||
"document_uri": prompt.document_uri,
|
||||
"detail_uri": f"resource://catalog/prompts/{prompt.prompt_id}",
|
||||
}
|
||||
|
||||
|
||||
def _skill_matches(
|
||||
skill: SkillRecord,
|
||||
*,
|
||||
@@ -72,6 +85,34 @@ def _skill_matches(
|
||||
return True
|
||||
|
||||
|
||||
def _prompt_matches(
|
||||
prompt: PromptRecord,
|
||||
*,
|
||||
query: str | None,
|
||||
tag: str | None,
|
||||
) -> bool:
|
||||
if query:
|
||||
lowered = query.strip().lower()
|
||||
if lowered:
|
||||
haystack = " ".join(
|
||||
[
|
||||
prompt.prompt_id,
|
||||
prompt.name,
|
||||
prompt.description,
|
||||
" ".join(prompt.tags),
|
||||
" ".join(sorted(prompt.arguments)),
|
||||
]
|
||||
).lower()
|
||||
terms = [term for term in lowered.replace("-", " ").split() if term]
|
||||
if any(term not in haystack for term in terms):
|
||||
return False
|
||||
|
||||
if tag and tag not in prompt.tags:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def build_skills_index_payload(
|
||||
registry: DocsRegistry,
|
||||
*,
|
||||
@@ -136,6 +177,64 @@ def build_skill_detail_payload(registry: DocsRegistry, skill_id: str) -> dict[st
|
||||
}
|
||||
|
||||
|
||||
def build_prompts_index_payload(
|
||||
registry: DocsRegistry,
|
||||
*,
|
||||
query: str | None = None,
|
||||
tag: str | None = None,
|
||||
cursor: str | None = None,
|
||||
limit: int | None = None,
|
||||
) -> dict[str, Any]:
|
||||
normalized_limit = DEFAULT_LIMIT if limit is None else max(1, min(limit, MAX_LIMIT))
|
||||
try:
|
||||
start = 0 if cursor is None else max(0, int(cursor))
|
||||
except ValueError as exc:
|
||||
raise ValueError("cursor must be an integer string") from exc
|
||||
|
||||
ordered = [
|
||||
registry.prompts_by_id[prompt_id]
|
||||
for prompt_id in registry.prompts_in_load_order
|
||||
]
|
||||
matches = [
|
||||
prompt for prompt in ordered if _prompt_matches(prompt, query=query, tag=tag)
|
||||
]
|
||||
|
||||
page = matches[start : start + normalized_limit]
|
||||
next_cursor = start + normalized_limit
|
||||
|
||||
return {
|
||||
"prompts": [_prompt_summary_payload(prompt) for prompt in page],
|
||||
"total": len(matches),
|
||||
"cursor": str(start),
|
||||
"limit": normalized_limit,
|
||||
"next_cursor": str(next_cursor) if next_cursor < len(matches) else None,
|
||||
}
|
||||
|
||||
|
||||
def build_prompt_detail_payload(
|
||||
registry: DocsRegistry, prompt_id: str
|
||||
) -> dict[str, Any]:
|
||||
if prompt_id not in registry.prompts_by_id:
|
||||
raise KeyError(prompt_id)
|
||||
|
||||
prompt = registry.prompts_by_id[prompt_id]
|
||||
return {
|
||||
"id": prompt.prompt_id,
|
||||
"name": prompt.name,
|
||||
"description": prompt.description,
|
||||
"version": prompt.version,
|
||||
"tags": list(prompt.tags),
|
||||
"capabilities": list(prompt.capabilities),
|
||||
"resources": {
|
||||
"document": prompt.document_uri,
|
||||
},
|
||||
"arguments": {
|
||||
arg_name: arg.model_dump(exclude_none=True)
|
||||
for arg_name, arg in sorted(prompt.arguments.items())
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def search_patterns_payload(
|
||||
registry: DocsRegistry,
|
||||
*,
|
||||
@@ -171,3 +270,43 @@ def get_pattern_by_id_payload(registry: DocsRegistry, skill_id: str) -> dict[str
|
||||
if skill_id not in registry.skills_by_id:
|
||||
return {"found": False, "id": skill_id}
|
||||
return {"found": True, "pattern": _pattern_payload(registry.skills_by_id[skill_id])}
|
||||
|
||||
|
||||
def search_prompts_payload(
|
||||
registry: DocsRegistry,
|
||||
*,
|
||||
query: str = "",
|
||||
tags: list[str] | None = None,
|
||||
skip: int = 0,
|
||||
limit: int = DEFAULT_LIMIT,
|
||||
) -> dict[str, Any]:
|
||||
normalized_skip = max(skip, 0)
|
||||
normalized_limit = max(1, min(limit, MAX_LIMIT))
|
||||
|
||||
requested_tags = [tag.strip() for tag in (tags or []) if tag and tag.strip()]
|
||||
|
||||
matches: list[PromptRecord] = []
|
||||
for prompt_id in registry.prompts_in_load_order:
|
||||
prompt = registry.prompts_by_id[prompt_id]
|
||||
if not _prompt_matches(prompt, query=query, tag=None):
|
||||
continue
|
||||
if requested_tags and any(tag not in prompt.tags for tag in requested_tags):
|
||||
continue
|
||||
matches.append(prompt)
|
||||
|
||||
page = matches[normalized_skip : normalized_skip + normalized_limit]
|
||||
return {
|
||||
"prompts": [_prompt_summary_payload(prompt) for prompt in page],
|
||||
"total": len(matches),
|
||||
"skip": normalized_skip,
|
||||
"limit": normalized_limit,
|
||||
}
|
||||
|
||||
|
||||
def get_prompt_by_id_payload(registry: DocsRegistry, prompt_id: str) -> dict[str, Any]:
|
||||
if prompt_id not in registry.prompts_by_id:
|
||||
return {"found": False, "id": prompt_id}
|
||||
return {
|
||||
"found": True,
|
||||
"prompt": build_prompt_detail_payload(registry, prompt_id),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user