add lil Preview

This commit is contained in:
lda
2026-05-19 03:37:48 +07:00 Verified
parent dddc7bb5eb
commit 0b2159bc9c
5 changed files with 99 additions and 3 deletions
+25
View File
@@ -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.
+3 -1
View File
@@ -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
+50
View File
@@ -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
+15 -2
View File
@@ -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:
+6
View File
@@ -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"))