docs(standards): vendor template into MCP bundle; add component tools + start_app prompt
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
"""# Summary
|
||||
|
||||
Example pipeline family — a reusable skeleton, not an app-specific pipeline.
|
||||
|
||||
Replace ``Example*`` names and the three stages with your family's stages. The
|
||||
shape (abc → impl → pipeline.yaml → resources/) stays the same.
|
||||
"""
|
||||
53
standards/mcp/templates/app/pipelines/example/abc.py
Normal file
53
standards/mcp/templates/app/pipelines/example/abc.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""# Summary
|
||||
|
||||
Example pipeline family — port contracts (stage interfaces + root interface).
|
||||
|
||||
Ports are declared as a DI tree: the root's annotated slots *are* the dependency
|
||||
decisions. The concrete subclass in ``impl.py`` pins the stage types.
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
from pipelines.shared.ports import FinalizePort
|
||||
|
||||
|
||||
class IngestPort(ABC):
|
||||
"""Obtain the raw input as text/bytes."""
|
||||
|
||||
@abstractmethod
|
||||
def materialize(self, source: Any) -> str:
|
||||
"""Return the raw input materialized as text."""
|
||||
|
||||
|
||||
class ReadPort(ABC):
|
||||
"""Parse materialized input into records."""
|
||||
|
||||
@abstractmethod
|
||||
def read(self, content: str) -> list[dict[str, Any]]:
|
||||
"""Return parsed records."""
|
||||
|
||||
|
||||
class NormalizePort(ABC):
|
||||
"""Normalize raw records into the target shape."""
|
||||
|
||||
@abstractmethod
|
||||
def build(self, records: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""Return normalized records."""
|
||||
|
||||
|
||||
class ExamplePipelinePort(ABC):
|
||||
"""Root of the example family.
|
||||
|
||||
Notes:
|
||||
The slot annotations below are contracts. A concrete subclass re-annotates
|
||||
them with stage implementations; the DI container instantiates the tree.
|
||||
"""
|
||||
|
||||
ingest: IngestPort
|
||||
reader: ReadPort
|
||||
normalize: NormalizePort
|
||||
finalize: FinalizePort
|
||||
|
||||
@abstractmethod
|
||||
async def run(self, source: Any) -> list[dict[str, Any]]:
|
||||
"""Run the full stage chain and return the normalized records."""
|
||||
71
standards/mcp/templates/app/pipelines/example/impl.py
Normal file
71
standards/mcp/templates/app/pipelines/example/impl.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""# Summary
|
||||
|
||||
Example pipeline family — concrete stages, the wired root, and its registration.
|
||||
|
||||
The concrete subclass re-annotates each slot with a stage implementation; the DI
|
||||
container builds the whole tree. Register the family so the shared registry can
|
||||
resolve it by ``(kind, variant, mode)``.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from pipelines.example.abc import (
|
||||
ExamplePipelinePort,
|
||||
IngestPort,
|
||||
NormalizePort,
|
||||
ReadPort,
|
||||
)
|
||||
from pipelines.shared.loader import build_pipeline
|
||||
from pipelines.shared.ports import FinalizePort
|
||||
from pipelines.shared.registry import register
|
||||
|
||||
|
||||
class TextIngest(IngestPort):
|
||||
"""Materialize any source as text."""
|
||||
|
||||
def materialize(self, source: Any) -> str:
|
||||
return source if isinstance(source, str) else str(source)
|
||||
|
||||
|
||||
class LineReader(ReadPort):
|
||||
"""Read one record per non-empty line."""
|
||||
|
||||
def read(self, content: str) -> list[dict[str, Any]]:
|
||||
return [{"line": line} for line in content.splitlines() if line.strip()]
|
||||
|
||||
|
||||
class PassthroughNormalize(NormalizePort):
|
||||
"""Return records unchanged (replace with real normalization)."""
|
||||
|
||||
def build(self, records: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
return records
|
||||
|
||||
|
||||
class NoopFinalize(FinalizePort):
|
||||
"""Drop records (replace with a service-backed finalize stage)."""
|
||||
|
||||
async def finalize(self, records: list[dict[str, Any]]) -> list[Any]:
|
||||
return records
|
||||
|
||||
|
||||
class ExamplePipeline(ExamplePipelinePort):
|
||||
"""Concrete example family — the dependency decisions live here."""
|
||||
|
||||
ingest: TextIngest
|
||||
reader: LineReader
|
||||
normalize: PassthroughNormalize
|
||||
finalize: NoopFinalize
|
||||
|
||||
async def run(self, source: Any) -> list[dict[str, Any]]:
|
||||
content = self.ingest.materialize(source)
|
||||
records = self.reader.read(content)
|
||||
return self.normalize.build(records)
|
||||
|
||||
|
||||
def get_example_pipeline() -> ExamplePipeline:
|
||||
"""Wire and return the example pipeline through the shared loader."""
|
||||
resources = Path(__file__).parent / "resources"
|
||||
return build_pipeline(ExamplePipeline, resources_dir=resources)
|
||||
|
||||
|
||||
register("example", "default", ExamplePipeline)
|
||||
18
standards/mcp/templates/app/pipelines/example/pipeline.yaml
Normal file
18
standards/mcp/templates/app/pipelines/example/pipeline.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
# Graph spec for the example family: nodes, edges, and the root.
|
||||
# Mirrors the port tree in abc.py. Replace with your family's graph.
|
||||
schema: ExampleRecord
|
||||
nodes:
|
||||
ingest:
|
||||
port: IngestPort
|
||||
reader:
|
||||
port: ReadPort
|
||||
normalize:
|
||||
port: NormalizePort
|
||||
finalize:
|
||||
port: FinalizePort
|
||||
edges:
|
||||
- ingest -> reader
|
||||
- reader -> normalize
|
||||
- normalize -> finalize
|
||||
roots:
|
||||
default: ExamplePipeline
|
||||
@@ -0,0 +1,4 @@
|
||||
# Pattern / rule fragments for the example family.
|
||||
# The loader merges every patterns/*.yaml into one registry by key.
|
||||
example:
|
||||
enabled: true
|
||||
@@ -0,0 +1,5 @@
|
||||
# Runtime spec consumed by the shared loader.
|
||||
kind: example
|
||||
variant: default
|
||||
mode: default
|
||||
patterns: []
|
||||
Reference in New Issue
Block a user