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:
- 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.
+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
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
+50
View File
@@ -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
+15 -2
View File
@@ -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:
+6
View File
@@ -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"))