add lil Preview
This commit is contained in:
@@ -305,9 +305,34 @@ the same admin/control capabilities.
|
||||
`list_sources()` is the compact source-discovery surface:
|
||||
|
||||
- returns paged source summaries with visibility, permissions, and counts
|
||||
- includes small sorted preview name lists per capability kind
|
||||
- includes `has_more` flags when a preview omits additional owned names
|
||||
- is intentionally compact enough for progressive discovery
|
||||
- pairs with `inspect_source(source_id)` for the full owned-capability inventory
|
||||
|
||||
Humans and LLM authoring clients should list sources first, then inspect only the
|
||||
sources they need. Planner projection remains a different **use** of source
|
||||
metadata, not a second source model.
|
||||
|
||||
The summary payload intentionally does not include schemas or executable
|
||||
contracts:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "wf.std",
|
||||
"node_spec_count": 12,
|
||||
"reducer_count": 6,
|
||||
"preview": {
|
||||
"node_specs": ["wf.std.coalesce", "wf.std.constant", "wf.std.default_if_none"],
|
||||
"reducers": ["wf.std.add", "wf.std.append", "wf.std.max"]
|
||||
},
|
||||
"has_more": {
|
||||
"node_specs": true,
|
||||
"reducers": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Call `inspect_source("wf.std")` only when those previews indicate the source is
|
||||
relevant. That keeps large source inventories usable for MCP clients with tight
|
||||
context windows.
|
||||
|
||||
@@ -170,7 +170,9 @@ wf.admin.get_planner_catalog
|
||||
```
|
||||
|
||||
Prefer `list_sources` first. It is the compact inventory. Inspect one source
|
||||
only when you need its full owned-capability list.
|
||||
only when you need its full owned-capability list. The compact response includes
|
||||
counts plus small preview lists and `has_more` flags, so clients can usually
|
||||
choose the next source to inspect without loading every schema.
|
||||
|
||||
### 4. Manage Saved Workflows
|
||||
|
||||
|
||||
@@ -75,6 +75,26 @@ class SourceCapabilityInventory(BaseModel):
|
||||
resources: tuple[str, ...] = ()
|
||||
|
||||
|
||||
class SourceCapabilityPreview(BaseModel):
|
||||
"""Small sorted capability-name sample for compact source discovery."""
|
||||
|
||||
tools: tuple[str, ...] = ()
|
||||
node_specs: tuple[str, ...] = ()
|
||||
reducers: tuple[str, ...] = ()
|
||||
prompts: tuple[str, ...] = ()
|
||||
resources: tuple[str, ...] = ()
|
||||
|
||||
|
||||
class SourceCapabilityHasMore(BaseModel):
|
||||
"""Whether each compact source preview omitted owned capabilities."""
|
||||
|
||||
tools: bool = False
|
||||
node_specs: bool = False
|
||||
reducers: bool = False
|
||||
prompts: bool = False
|
||||
resources: bool = False
|
||||
|
||||
|
||||
class SourceStatus(BaseModel):
|
||||
"""Serializable source metadata without the full owned-name inventory."""
|
||||
|
||||
@@ -89,6 +109,8 @@ class SourceStatus(BaseModel):
|
||||
reducer_count: int
|
||||
prompt_count: int
|
||||
resource_count: int
|
||||
preview: SourceCapabilityPreview
|
||||
has_more: SourceCapabilityHasMore
|
||||
|
||||
|
||||
class SourceInventory(SourceStatus):
|
||||
@@ -119,6 +141,7 @@ class CapabilitySource:
|
||||
|
||||
def as_status(self) -> SourceStatus:
|
||||
"""Return serializable source metadata without owned capability names."""
|
||||
preview_limit = 3
|
||||
return SourceStatus(
|
||||
id=self.id,
|
||||
kind=self.kind,
|
||||
@@ -140,6 +163,23 @@ class CapabilitySource:
|
||||
reducer_count=len(self.capabilities.reducers),
|
||||
prompt_count=len(self.capabilities.prompts),
|
||||
resource_count=len(self.capabilities.resources),
|
||||
preview=SourceCapabilityPreview(
|
||||
tools=_preview_names(self.capabilities.tools, preview_limit),
|
||||
node_specs=_preview_names(
|
||||
self.capabilities.node_specs,
|
||||
preview_limit,
|
||||
),
|
||||
reducers=_preview_names(self.capabilities.reducers, preview_limit),
|
||||
prompts=_preview_names(self.capabilities.prompts, preview_limit),
|
||||
resources=_preview_names(self.capabilities.resources, preview_limit),
|
||||
),
|
||||
has_more=SourceCapabilityHasMore(
|
||||
tools=_has_more(self.capabilities.tools, preview_limit),
|
||||
node_specs=_has_more(self.capabilities.node_specs, preview_limit),
|
||||
reducers=_has_more(self.capabilities.reducers, preview_limit),
|
||||
prompts=_has_more(self.capabilities.prompts, preview_limit),
|
||||
resources=_has_more(self.capabilities.resources, preview_limit),
|
||||
),
|
||||
)
|
||||
|
||||
def as_inventory(self) -> SourceInventory:
|
||||
@@ -186,3 +226,13 @@ def _node_spec_inventory(spec: NodeSpec[Any, Any]) -> NodeSpecInventory:
|
||||
is_async=spec.is_async,
|
||||
accepts_context=spec.accepts_context,
|
||||
)
|
||||
|
||||
|
||||
def _preview_names(values: dict[str, Any], limit: int) -> tuple[str, ...]:
|
||||
"""Return a tiny deterministic sample so list views stay inspectable."""
|
||||
return tuple(sorted(values)[:limit])
|
||||
|
||||
|
||||
def _has_more(values: dict[str, Any], limit: int) -> bool:
|
||||
"""Return whether the compact preview omitted owned capability names."""
|
||||
return len(values) > limit
|
||||
|
||||
@@ -18,7 +18,12 @@ def test_capability_source_projects_typed_status() -> None:
|
||||
id="wf.std",
|
||||
kind="system",
|
||||
capabilities=CapabilityBuckets(
|
||||
tools={"wf.std.inspect": object()},
|
||||
tools={
|
||||
"wf.std.inspect": object(),
|
||||
"wf.std.zeta": object(),
|
||||
"wf.std.alpha": object(),
|
||||
"wf.std.beta": object(),
|
||||
},
|
||||
resources={"wf.std.manual": object()},
|
||||
),
|
||||
visibility=SourceVisibility(planner=True, mcp_client=True),
|
||||
@@ -32,8 +37,16 @@ def test_capability_source_projects_typed_status() -> None:
|
||||
assert status.id == "wf.std"
|
||||
assert status.visibility.planner is True
|
||||
assert status.permissions.safe_for_workflow is True
|
||||
assert status.tool_count == 1
|
||||
assert status.tool_count == 4
|
||||
assert status.preview.tools == (
|
||||
"wf.std.alpha",
|
||||
"wf.std.beta",
|
||||
"wf.std.inspect",
|
||||
)
|
||||
assert status.has_more.tools is True
|
||||
assert status.resource_count == 1
|
||||
assert status.preview.resources == ("wf.std.manual",)
|
||||
assert status.has_more.resources is False
|
||||
|
||||
|
||||
def test_capability_source_projects_typed_inventory() -> None:
|
||||
|
||||
@@ -153,6 +153,12 @@ def test_service_lists_compact_source_summaries() -> None:
|
||||
assert payload["next_cursor"] == "2"
|
||||
assert "capabilities" not in payload["sources"][0]
|
||||
|
||||
full_page = service.list_source_summaries(limit=100)
|
||||
sources_by_id = {source["id"]: source for source in full_page["sources"]}
|
||||
std_source = sources_by_id["wf.std"]
|
||||
assert "wf.std.coalesce" in std_source["preview"]["node_specs"]
|
||||
assert std_source["has_more"]["node_specs"] is True
|
||||
|
||||
|
||||
def test_wf_std_source_contains_authoring_ops() -> None:
|
||||
service = WfMcpService(store=FileStore(local_temp_root() / "stdlib_source_store"))
|
||||
|
||||
Reference in New Issue
Block a user