This commit is contained in:
lda
2026-05-18 04:08:01 +07:00 Verified
parent 415c0315c6
commit 9dca18fafd
4 changed files with 55 additions and 1 deletions
+3
View File
@@ -198,6 +198,9 @@ The code now has the first capability-source layer in place.
Python callables. Inventory exposes `NodeSpecInventory` contracts instead: Python callables. Inventory exposes `NodeSpecInventory` contracts instead:
names, descriptions, outcomes, schemas, and execution flags, but never the names, descriptions, outcomes, schemas, and execution flags, but never the
wrapped function object itself. wrapped function object itself.
- Reducer inventory follows the same rule: executable reducer definitions stay
private, while `ReducerInventory` exposes only reducer names, descriptions,
and config schemas.
- `WfMcpService.capability_sources` is the canonical in-memory registry. - `WfMcpService.capability_sources` is the canonical in-memory registry.
- Planner node lookup reads `CapabilitySource.capabilities.node_specs` - Planner node lookup reads `CapabilitySource.capabilities.node_specs`
directly; the old `SpecSource` compatibility layer has been removed. directly; the old `SpecSource` compatibility layer has been removed.
+2
View File
@@ -4,6 +4,7 @@ from .sources import (
CapabilityBuckets, CapabilityBuckets,
CapabilitySource, CapabilitySource,
NodeSpecInventory, NodeSpecInventory,
ReducerInventory,
SourceCapabilityInventory, SourceCapabilityInventory,
SourceInventory, SourceInventory,
SourceKind, SourceKind,
@@ -19,6 +20,7 @@ __all__ = [
"CapabilitySource", "CapabilitySource",
"CapabilityRef", "CapabilityRef",
"NodeSpecInventory", "NodeSpecInventory",
"ReducerInventory",
"SourceCapabilityInventory", "SourceCapabilityInventory",
"SourceInventory", "SourceInventory",
"SourceKind", "SourceKind",
+20
View File
@@ -55,6 +55,14 @@ class NodeSpecInventory(BaseModel):
accepts_context: bool accepts_context: bool
class ReducerInventory(BaseModel):
"""Serializable public contract for one pure reducer."""
name: str
description: str | None = None
config_schema: JsonObject
class SourceCapabilityInventory(BaseModel): class SourceCapabilityInventory(BaseModel):
"""Serializable names owned by one source, grouped by capability kind.""" """Serializable names owned by one source, grouped by capability kind."""
@@ -62,6 +70,7 @@ class SourceCapabilityInventory(BaseModel):
node_specs: tuple[str, ...] = () node_specs: tuple[str, ...] = ()
node_spec_details: tuple[NodeSpecInventory, ...] = () node_spec_details: tuple[NodeSpecInventory, ...] = ()
reducers: tuple[str, ...] = () reducers: tuple[str, ...] = ()
reducer_details: tuple[ReducerInventory, ...] = ()
prompts: tuple[str, ...] = () prompts: tuple[str, ...] = ()
resources: tuple[str, ...] = () resources: tuple[str, ...] = ()
@@ -148,6 +157,17 @@ class CapabilitySource:
) )
), ),
reducers=tuple(sorted(self.capabilities.reducers)), reducers=tuple(sorted(self.capabilities.reducers)),
reducer_details=tuple(
ReducerInventory(
name=reducer.name,
description=reducer.description,
config_schema=reducer.config_schema,
)
for reducer in sorted(
self.capabilities.reducers.values(),
key=lambda reducer: reducer.name,
)
),
prompts=tuple(sorted(self.capabilities.prompts)), prompts=tuple(sorted(self.capabilities.prompts)),
resources=tuple(sorted(self.capabilities.resources)), resources=tuple(sorted(self.capabilities.resources)),
), ),
+30 -1
View File
@@ -3,7 +3,13 @@ from __future__ import annotations
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from wf_authoring import node from wf_authoring import node
from wf_platform import CapabilityBuckets, CapabilitySource, NodeSpecInventory from wf_core import ReducerSpec
from wf_platform import (
CapabilityBuckets,
CapabilitySource,
NodeSpecInventory,
ReducerInventory,
)
class EchoInput(BaseModel): class EchoInput(BaseModel):
@@ -37,3 +43,26 @@ def test_source_inventory_exposes_serializable_node_spec_details() -> None:
assert detail.outcomes == ("ok",) assert detail.outcomes == ("ok",)
assert detail.input_schema["properties"]["text"]["description"] == "Text to echo." assert detail.input_schema["properties"]["text"]["description"] == "Text to echo."
assert "fn" not in dumped["capabilities"]["node_spec_details"][0] assert "fn" not in dumped["capabilities"]["node_spec_details"][0]
def test_source_inventory_exposes_serializable_reducer_details() -> None:
source = CapabilitySource(
id="wf.std",
kind="system",
capabilities=CapabilityBuckets(
reducers={
"wf.std.max": ReducerSpec(
name="wf.std.max",
description="Keep the greater value.",
config_schema={"type": "object", "properties": {}},
)
}
),
)
detail = source.as_inventory().capabilities.reducer_details[0]
assert isinstance(detail, ReducerInventory)
assert detail.name == "wf.std.max"
assert detail.description == "Keep the greater value."
assert detail.config_schema == {"type": "object", "properties": {}}