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:
|
`list_sources()` is the compact source-discovery surface:
|
||||||
|
|
||||||
- returns paged source summaries with visibility, permissions, and counts
|
- 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
|
- is intentionally compact enough for progressive discovery
|
||||||
- pairs with `inspect_source(source_id)` for the full owned-capability inventory
|
- 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
|
Humans and LLM authoring clients should list sources first, then inspect only the
|
||||||
sources they need. Planner projection remains a different **use** of source
|
sources they need. Planner projection remains a different **use** of source
|
||||||
metadata, not a second source model.
|
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
|
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
|
### 4. Manage Saved Workflows
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,26 @@ class SourceCapabilityInventory(BaseModel):
|
|||||||
resources: tuple[str, ...] = ()
|
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):
|
class SourceStatus(BaseModel):
|
||||||
"""Serializable source metadata without the full owned-name inventory."""
|
"""Serializable source metadata without the full owned-name inventory."""
|
||||||
|
|
||||||
@@ -89,6 +109,8 @@ class SourceStatus(BaseModel):
|
|||||||
reducer_count: int
|
reducer_count: int
|
||||||
prompt_count: int
|
prompt_count: int
|
||||||
resource_count: int
|
resource_count: int
|
||||||
|
preview: SourceCapabilityPreview
|
||||||
|
has_more: SourceCapabilityHasMore
|
||||||
|
|
||||||
|
|
||||||
class SourceInventory(SourceStatus):
|
class SourceInventory(SourceStatus):
|
||||||
@@ -119,6 +141,7 @@ class CapabilitySource:
|
|||||||
|
|
||||||
def as_status(self) -> SourceStatus:
|
def as_status(self) -> SourceStatus:
|
||||||
"""Return serializable source metadata without owned capability names."""
|
"""Return serializable source metadata without owned capability names."""
|
||||||
|
preview_limit = 3
|
||||||
return SourceStatus(
|
return SourceStatus(
|
||||||
id=self.id,
|
id=self.id,
|
||||||
kind=self.kind,
|
kind=self.kind,
|
||||||
@@ -140,6 +163,23 @@ class CapabilitySource:
|
|||||||
reducer_count=len(self.capabilities.reducers),
|
reducer_count=len(self.capabilities.reducers),
|
||||||
prompt_count=len(self.capabilities.prompts),
|
prompt_count=len(self.capabilities.prompts),
|
||||||
resource_count=len(self.capabilities.resources),
|
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:
|
def as_inventory(self) -> SourceInventory:
|
||||||
@@ -186,3 +226,13 @@ def _node_spec_inventory(spec: NodeSpec[Any, Any]) -> NodeSpecInventory:
|
|||||||
is_async=spec.is_async,
|
is_async=spec.is_async,
|
||||||
accepts_context=spec.accepts_context,
|
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",
|
id="wf.std",
|
||||||
kind="system",
|
kind="system",
|
||||||
capabilities=CapabilityBuckets(
|
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()},
|
resources={"wf.std.manual": object()},
|
||||||
),
|
),
|
||||||
visibility=SourceVisibility(planner=True, mcp_client=True),
|
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.id == "wf.std"
|
||||||
assert status.visibility.planner is True
|
assert status.visibility.planner is True
|
||||||
assert status.permissions.safe_for_workflow 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.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:
|
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 payload["next_cursor"] == "2"
|
||||||
assert "capabilities" not in payload["sources"][0]
|
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:
|
def test_wf_std_source_contains_authoring_ops() -> None:
|
||||||
service = WfMcpService(store=FileStore(local_temp_root() / "stdlib_source_store"))
|
service = WfMcpService(store=FileStore(local_temp_root() / "stdlib_source_store"))
|
||||||
|
|||||||
Reference in New Issue
Block a user