feat: add standards wiki, MCP registry, and app template
This commit is contained in:
6
mcp/hexa/index.json
Normal file
6
mcp/hexa/index.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"project": "hexa",
|
||||
"type": "docforge-model",
|
||||
"modules_count": 9,
|
||||
"source": "docforge"
|
||||
}
|
||||
102
mcp/hexa/modules/hexa.check_matches.json
Normal file
102
mcp/hexa/modules/hexa.check_matches.json
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"module": "hexa.check_matches",
|
||||
"content": {
|
||||
"path": "hexa.check_matches",
|
||||
"docstring": "# Summary\n\nVerify that two hexa representations agree.\n\n``check_matches`` parses two representations — one YAML spec and/or ABC Python\nmodule each — and reports whether they describe the same port tree.\n\n---\n# Notes\n\n- Uses ``PortNode.diff`` to produce human-readable difference strings.",
|
||||
"objects": {
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.check_matches.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse_abc": {
|
||||
"name": "parse_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.parse_abc",
|
||||
"signature": "parse_abc(module_or_path: str | Path | Any)",
|
||||
"docstring": "Parse an ABC Python module into a :class:`PortNode` tree.\n\nABC classes become port nodes. A nested-struct attribute is expressed as a\n``@dataclass`` annotation on an ABC port (e.g. ``source: ExtractionSource``\nwhere ``ExtractionSource`` is a ``@dataclass``); the dataclass's fields are\nexpanded into a nested ``attributes`` entry.\n\nArgs:\n module_or_path (str | Path | Any):\n A file path to a ``.py`` file, a module import string, or a loaded\n module.\n\nReturns:\n PortNode:\n The root node of the tree."
|
||||
},
|
||||
"parse_yaml": {
|
||||
"name": "parse_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.parse_yaml",
|
||||
"signature": "parse_yaml(path: str | Path)",
|
||||
"docstring": "Parse a hexa YAML spec file into a :class:`PortNode` tree.\n\nArgs:\n path (str | Path):\n Path to the ``.yaml`` file.\n\nReturns:\n PortNode:\n The root node of the parsed tree.\n\nRaises:\n ValueError:\n On structural errors in the YAML."
|
||||
},
|
||||
"check_matches": {
|
||||
"name": "check_matches",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.check_matches",
|
||||
"signature": "check_matches(path_a: str | Path, path_b: str | Path) -> bool | list[str]",
|
||||
"docstring": "Verify two representations agree.\n\nArgs:\n path_a (str | Path):\n Path to a ``.yaml`` or ``.py`` file.\n\n path_b (str | Path):\n Path to a ``.yaml`` or ``.py`` file.\n\nReturns:\n bool | list[str]:\n ``True`` if they match exactly, otherwise a list of difference\n strings."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
mcp/hexa/modules/hexa.cli.json
Normal file
51
mcp/hexa/modules/hexa.cli.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"module": "hexa.cli",
|
||||
"content": {
|
||||
"path": "hexa.cli",
|
||||
"docstring": "# Summary\n\nCommand-line interface for hexa utilities.\n\n---\n# Usage\n\n hexa parse-yaml PATH\n hexa generate-abc PATH [--out FILE]\n hexa parse-abc PATH\n hexa generate-yaml PATH [--out FILE]\n hexa check PATH_A PATH_B",
|
||||
"objects": {
|
||||
"check_matches": {
|
||||
"name": "check_matches",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.check_matches",
|
||||
"signature": "check_matches(path_a: str | Path, path_b: str | Path)",
|
||||
"docstring": "Verify two representations agree.\n\nArgs:\n path_a (str | Path):\n Path to a ``.yaml`` or ``.py`` file.\n\n path_b (str | Path):\n Path to a ``.yaml`` or ``.py`` file.\n\nReturns:\n bool | list[str]:\n ``True`` if they match exactly, otherwise a list of difference\n strings."
|
||||
},
|
||||
"generate_abc": {
|
||||
"name": "generate_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.generate_abc",
|
||||
"signature": "generate_abc(node: PortNode, out_path: str | Path | None = None)",
|
||||
"docstring": "Generate ABC Python source from a :class:`PortNode` tree.\n\nArgs:\n node (PortNode):\n The root of the parsed YAML (or generated) tree.\n\n out_path (str | Path | None):\n Optional file path to write the generated source to.\n\nReturns:\n str:\n The generated Python source code."
|
||||
},
|
||||
"generate_yaml": {
|
||||
"name": "generate_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.generate_yaml",
|
||||
"signature": "generate_yaml(node: PortNode, out_path: str | Path | None = None)",
|
||||
"docstring": "Generate YAML source from a :class:`PortNode` tree.\n\nArgs:\n node (PortNode):\n The root of the tree.\n\n out_path (str | Path | None):\n Optional file path to write the generated source to.\n\nReturns:\n str:\n The generated YAML source."
|
||||
},
|
||||
"parse_abc": {
|
||||
"name": "parse_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.parse_abc",
|
||||
"signature": "parse_abc(module_or_path: str | Path | Any)",
|
||||
"docstring": "Parse an ABC Python module into a :class:`PortNode` tree.\n\nABC classes become port nodes. A nested-struct attribute is expressed as a\n``@dataclass`` annotation on an ABC port (e.g. ``source: ExtractionSource``\nwhere ``ExtractionSource`` is a ``@dataclass``); the dataclass's fields are\nexpanded into a nested ``attributes`` entry.\n\nArgs:\n module_or_path (str | Path | Any):\n A file path to a ``.py`` file, a module import string, or a loaded\n module.\n\nReturns:\n PortNode:\n The root node of the tree."
|
||||
},
|
||||
"parse_yaml": {
|
||||
"name": "parse_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.parse_yaml",
|
||||
"signature": "parse_yaml(path: str | Path)",
|
||||
"docstring": "Parse a hexa YAML spec file into a :class:`PortNode` tree.\n\nArgs:\n path (str | Path):\n Path to the ``.yaml`` file.\n\nReturns:\n PortNode:\n The root node of the parsed tree.\n\nRaises:\n ValueError:\n On structural errors in the YAML."
|
||||
},
|
||||
"main": {
|
||||
"name": "main",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.main",
|
||||
"signature": "main() -> None",
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
mcp/hexa/modules/hexa.container.json
Normal file
23
mcp/hexa/modules/hexa.container.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"module": "hexa.container",
|
||||
"content": {
|
||||
"path": "hexa.container",
|
||||
"docstring": "# Summary\n\nContainer to build a wired hexa pipeline from annotations.\n\n``build`` recursively instantiates the port tree implied by a root class's\nannotations, injecting provided runtime instances and config values by name.\n\n---\n# Notes\n\n- Runtime instances and config values are applied by name across the whole tree.",
|
||||
"objects": {
|
||||
"T": {
|
||||
"name": "T",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.container.T",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"build": {
|
||||
"name": "build",
|
||||
"kind": "function",
|
||||
"path": "hexa.container.build",
|
||||
"signature": "build(cls: type[T], *, instances: dict[str, Any] | None = None, config: dict[str, Any] | None = None) -> T",
|
||||
"docstring": "Recursively instantiate the port tree from annotations.\n\nArgs:\n cls (type[T]):\n The root class to build (e.g. ``AxisExtractionPipeline``).\n\n instances (dict[str, Any] | None):\n ``{slot_name: object}`` injected verbatim into every node whose\n annotations contain that name (runtime values — repos, handlers,\n clients — that must not be re-constructed). Applied to the whole\n tree by name.\n\n config (dict[str, Any] | None):\n ``{field_name: value}`` propagated by name to every node's\n annotated config fields (non-port attributes), replacing the\n default ``None``.\n\nReturns:\n T:\n A fully wired instance of ``cls`` and all its nested ports."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
mcp/hexa/modules/hexa.generate_abc.json
Normal file
95
mcp/hexa/modules/hexa.generate_abc.json
Normal file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"module": "hexa.generate_abc",
|
||||
"content": {
|
||||
"path": "hexa.generate_abc",
|
||||
"docstring": "# Summary\n\nGenerate ``_abc.py`` source from a hexa PortNode tree.",
|
||||
"objects": {
|
||||
"NestedConfig": {
|
||||
"name": "NestedConfig",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.NestedConfig",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.generate_abc.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_abc.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_abc.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_abc.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"generate_abc": {
|
||||
"name": "generate_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_abc.generate_abc",
|
||||
"signature": "generate_abc(node: PortNode, out_path: str | Path | None = None) -> str",
|
||||
"docstring": "Generate ABC Python source from a :class:`PortNode` tree.\n\nArgs:\n node (PortNode):\n The root of the parsed YAML (or generated) tree.\n\n out_path (str | Path | None):\n Optional file path to write the generated source to.\n\nReturns:\n str:\n The generated Python source code."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
mcp/hexa/modules/hexa.generate_yaml.json
Normal file
125
mcp/hexa/modules/hexa.generate_yaml.json
Normal file
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"module": "hexa.generate_yaml",
|
||||
"content": {
|
||||
"path": "hexa.generate_yaml",
|
||||
"docstring": "# Summary\n\nGenerate YAML spec (nested mapping form) from a hexa PortNode tree.",
|
||||
"objects": {
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.generate_yaml.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"NestedConfig": {
|
||||
"name": "NestedConfig",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.NestedConfig",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.generate_yaml.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_yaml.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_yaml.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_yaml.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"generate_yaml": {
|
||||
"name": "generate_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_yaml.generate_yaml",
|
||||
"signature": "generate_yaml(node: PortNode, out_path: str | Path | None = None) -> str",
|
||||
"docstring": "Generate YAML source from a :class:`PortNode` tree.\n\nArgs:\n node (PortNode):\n The root of the tree.\n\n out_path (str | Path | None):\n Optional file path to write the generated source to.\n\nReturns:\n str:\n The generated YAML source."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
889
mcp/hexa/modules/hexa.json
Normal file
889
mcp/hexa/modules/hexa.json
Normal file
@@ -0,0 +1,889 @@
|
||||
{
|
||||
"module": "hexa",
|
||||
"content": {
|
||||
"path": "hexa",
|
||||
"docstring": "# Summary\n\nType-declared dependency injection for Python.\n\nHexa is a pattern and utility library for structuring complex hierarchical\npipelines. It rests on one core idea: a concrete class's annotated slot type\n*is* the dependency decision. Variation is expressed entirely as class\nannotations — never as imperative wiring, never as ``__init__`` parameters,\nnever as a composition root that assembles objects by hand.\n\nAll utilities serialize through a single shared model (``PortNode``):\n\n- ``parse_yaml`` / ``generate_yaml`` — between YAML specs and the model\n- ``parse_abc`` / ``generate_abc`` — between ABC Python modules and the model\n- ``check_matches`` — verify two representations describe the same tree\n- ``build`` — recursively instantiate a wired pipeline from annotations\n\n---\n# Installation\n\nInstall using pip:\n\n```bash\npip install hexa\n```\n\n---\n# Quick start\n\nDefine contracts, pin concrete types on the slots, and let ``build`` wire the\ntree from annotations:\n\n```python\nfrom hexa import build\n\nclass NumberPort(ABC):\n @abstractmethod\n def extract(self, line: str) -> float: ...\n\nclass ParserPort(ABC):\n number: NumberPort\n\n @abstractmethod\n def parse(self, text: str) -> dict: ...\n\nclass RegexNumberPort(NumberPort):\n def extract(self, line: str) -> float:\n ...\n\nclass StandardParserPort(ParserPort):\n number: RegexNumberPort # <-- the dependency decision\n\n def parse(self, text: str) -> dict:\n return {\"value\": self.number.extract(text)}\n\npipeline = build(StandardParserPort)\nprint(pipeline.parse(\"Value: 42\"))\n```\n\n---\n# CLI usage\n\nConvert between YAML and ABC representations:\n\n```bash\nhexa parse-yaml spec.yaml\nhexa generate-abc spec.yaml --out spec_abc.py\nhexa parse-abc spec_abc.py\nhexa generate-yaml spec_abc.py --out spec.yaml\nhexa check spec.yaml spec_abc.py\n```\n\n---\n# Core concepts\n\n## Shared model\n``PortNode`` — the neutral tree every parser and generator serializes through.\n\n## Converters\nParsers and generators that translate between YAML specs and ABC Python\nmodules via the shared model.\n\n## Verification\n``check_matches`` — machine-check that two representations agree.\n\n## Container\n``build`` — recursively instantiate a full pipeline from class annotations.\n\n---\n# Notes\n\n- All utilities share a single model; any port tree fits.\n- Generators emit source text only; hexa performs no runtime code generation.",
|
||||
"objects": {
|
||||
"check_matches": {
|
||||
"name": "check_matches",
|
||||
"kind": "module",
|
||||
"path": "hexa.check_matches",
|
||||
"signature": null,
|
||||
"docstring": "# Summary\n\nVerify that two hexa representations agree.\n\n``check_matches`` parses two representations — one YAML spec and/or ABC Python\nmodule each — and reports whether they describe the same port tree.\n\n---\n# Notes\n\n- Uses ``PortNode.diff`` to produce human-readable difference strings.",
|
||||
"members": {
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.check_matches.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.check_matches.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse_abc": {
|
||||
"name": "parse_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.parse_abc",
|
||||
"signature": "parse_abc(module_or_path: str | Path | Any)",
|
||||
"docstring": "Parse an ABC Python module into a :class:`PortNode` tree.\n\nABC classes become port nodes. A nested-struct attribute is expressed as a\n``@dataclass`` annotation on an ABC port (e.g. ``source: ExtractionSource``\nwhere ``ExtractionSource`` is a ``@dataclass``); the dataclass's fields are\nexpanded into a nested ``attributes`` entry.\n\nArgs:\n module_or_path (str | Path | Any):\n A file path to a ``.py`` file, a module import string, or a loaded\n module.\n\nReturns:\n PortNode:\n The root node of the tree."
|
||||
},
|
||||
"parse_yaml": {
|
||||
"name": "parse_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.parse_yaml",
|
||||
"signature": "parse_yaml(path: str | Path)",
|
||||
"docstring": "Parse a hexa YAML spec file into a :class:`PortNode` tree.\n\nArgs:\n path (str | Path):\n Path to the ``.yaml`` file.\n\nReturns:\n PortNode:\n The root node of the parsed tree.\n\nRaises:\n ValueError:\n On structural errors in the YAML."
|
||||
},
|
||||
"check_matches": {
|
||||
"name": "check_matches",
|
||||
"kind": "function",
|
||||
"path": "hexa.check_matches.check_matches",
|
||||
"signature": "check_matches(path_a: str | Path, path_b: str | Path) -> bool | list[str]",
|
||||
"docstring": "Verify two representations agree.\n\nArgs:\n path_a (str | Path):\n Path to a ``.yaml`` or ``.py`` file.\n\n path_b (str | Path):\n Path to a ``.yaml`` or ``.py`` file.\n\nReturns:\n bool | list[str]:\n ``True`` if they match exactly, otherwise a list of difference\n strings."
|
||||
}
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"name": "build",
|
||||
"kind": "function",
|
||||
"path": "hexa.build",
|
||||
"signature": "build(cls: type[T], *, instances: dict[str, Any] | None = None, config: dict[str, Any] | None = None)",
|
||||
"docstring": "Recursively instantiate the port tree from annotations.\n\nArgs:\n cls (type[T]):\n The root class to build (e.g. ``AxisExtractionPipeline``).\n\n instances (dict[str, Any] | None):\n ``{slot_name: object}`` injected verbatim into every node whose\n annotations contain that name (runtime values — repos, handlers,\n clients — that must not be re-constructed). Applied to the whole\n tree by name.\n\n config (dict[str, Any] | None):\n ``{field_name: value}`` propagated by name to every node's\n annotated config fields (non-port attributes), replacing the\n default ``None``.\n\nReturns:\n T:\n A fully wired instance of ``cls`` and all its nested ports."
|
||||
},
|
||||
"generate_abc": {
|
||||
"name": "generate_abc",
|
||||
"kind": "module",
|
||||
"path": "hexa.generate_abc",
|
||||
"signature": null,
|
||||
"docstring": "# Summary\n\nGenerate ``_abc.py`` source from a hexa PortNode tree.",
|
||||
"members": {
|
||||
"NestedConfig": {
|
||||
"name": "NestedConfig",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.NestedConfig",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.generate_abc.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_abc.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_abc.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_abc.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_abc.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"generate_abc": {
|
||||
"name": "generate_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_abc.generate_abc",
|
||||
"signature": "generate_abc(node: PortNode, out_path: str | Path | None = None) -> str",
|
||||
"docstring": "Generate ABC Python source from a :class:`PortNode` tree.\n\nArgs:\n node (PortNode):\n The root of the parsed YAML (or generated) tree.\n\n out_path (str | Path | None):\n Optional file path to write the generated source to.\n\nReturns:\n str:\n The generated Python source code."
|
||||
}
|
||||
}
|
||||
},
|
||||
"generate_yaml": {
|
||||
"name": "generate_yaml",
|
||||
"kind": "module",
|
||||
"path": "hexa.generate_yaml",
|
||||
"signature": null,
|
||||
"docstring": "# Summary\n\nGenerate YAML spec (nested mapping form) from a hexa PortNode tree.",
|
||||
"members": {
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.generate_yaml.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"NestedConfig": {
|
||||
"name": "NestedConfig",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.NestedConfig",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.generate_yaml.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.generate_yaml.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_yaml.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_yaml.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_yaml.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"generate_yaml": {
|
||||
"name": "generate_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.generate_yaml.generate_yaml",
|
||||
"signature": "generate_yaml(node: PortNode, out_path: str | Path | None = None) -> str",
|
||||
"docstring": "Generate YAML source from a :class:`PortNode` tree.\n\nArgs:\n node (PortNode):\n The root of the tree.\n\n out_path (str | Path | None):\n Optional file path to write the generated source to.\n\nReturns:\n str:\n The generated YAML source."
|
||||
}
|
||||
}
|
||||
},
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse_abc": {
|
||||
"name": "parse_abc",
|
||||
"kind": "module",
|
||||
"path": "hexa.parse_abc",
|
||||
"signature": null,
|
||||
"docstring": "# Summary\n\nParse an ABC Python module into a hexa PortNode tree.\n\nABC classes become port nodes. A nested-struct attribute is expressed as a\n``@dataclass`` annotation on an ABC port (e.g. ``source: ExtractionSource``\nwhere ``ExtractionSource`` is a ``@dataclass``); the dataclass's fields are\nexpanded into a nested ``attributes`` entry.\n\n---\n# Notes\n\n- Accepts a file path, a module import string, or an already-loaded module.",
|
||||
"members": {
|
||||
"ConfigValue": {
|
||||
"name": "ConfigValue",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.ConfigValue",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.parse_abc.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.parse_abc.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_abc.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_abc.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_abc.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse_abc": {
|
||||
"name": "parse_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_abc.parse_abc",
|
||||
"signature": "parse_abc(module_or_path: str | Path | Any) -> PortNode",
|
||||
"docstring": "Parse an ABC Python module into a :class:`PortNode` tree.\n\nABC classes become port nodes. A nested-struct attribute is expressed as a\n``@dataclass`` annotation on an ABC port (e.g. ``source: ExtractionSource``\nwhere ``ExtractionSource`` is a ``@dataclass``); the dataclass's fields are\nexpanded into a nested ``attributes`` entry.\n\nArgs:\n module_or_path (str | Path | Any):\n A file path to a ``.py`` file, a module import string, or a loaded\n module.\n\nReturns:\n PortNode:\n The root node of the tree."
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse_yaml": {
|
||||
"name": "parse_yaml",
|
||||
"kind": "module",
|
||||
"path": "hexa.parse_yaml",
|
||||
"signature": null,
|
||||
"docstring": "# Summary\n\nParse a hexa YAML spec into a :class:`PortNode` tree.\n\nThe YAML grammar uses a nested mapping format built from three member kinds:\n\n- **attribute**: a ``name: type`` scalar (e.g. ``source_type: str``)\n- **nested struct**: a ``name:`` mapping of sub-attributes (e.g. ``source:``)\n- **port**: a ``name:`` mapping whose single key is a class whose value is a\n body, or a class key with a ``...`` body. A port carries a class shape.\n\nDisambiguation of a mapping value:\n A mapping with exactly one key whose value is a non-scalar body (a mapping,\n ``...``, or a list) is a **port**. Any other mapping (multiple keys, or all\n scalar-typed values) is a **nested struct** of attributes.\n\n---\n# Examples\n\nParse a spec into a port tree:\n\n ```python\n from hexa import parse_yaml\n\n node = parse_yaml(\"samples/minimal/sample.yaml\")\n print(node)\n ```\n\nThe YAML grammar:\n\n ```yaml\n RootClassName:\n attr: type # scalar attribute\n source: # nested struct (all scalar sub-attributes)\n source_type: str\n bank: str\n methods: # reserved block of functions\n run:\n args: [a, b]\n ingest: # port (single non-scalar class key)\n IngestPort:\n source_type: str\n methods: {Method: ...}\n ```\n\n---\n# Notes\n\n- See ``samples/minimal/sample.yaml`` and\n ``samples/extraction_pipeline/extraction_pipeline.yaml`` for worked examples.",
|
||||
"members": {
|
||||
"ConfigValue": {
|
||||
"name": "ConfigValue",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.ConfigValue",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.parse_yaml.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.parse_yaml.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_yaml.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_yaml.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_yaml.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse_yaml": {
|
||||
"name": "parse_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_yaml.parse_yaml",
|
||||
"signature": "parse_yaml(path: str | Path) -> PortNode",
|
||||
"docstring": "Parse a hexa YAML spec file into a :class:`PortNode` tree.\n\nArgs:\n path (str | Path):\n Path to the ``.yaml`` file.\n\nReturns:\n PortNode:\n The root node of the parsed tree.\n\nRaises:\n ValueError:\n On structural errors in the YAML."
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"name": "cli",
|
||||
"kind": "module",
|
||||
"path": "hexa.cli",
|
||||
"signature": null,
|
||||
"docstring": "# Summary\n\nCommand-line interface for hexa utilities.\n\n---\n# Usage\n\n hexa parse-yaml PATH\n hexa generate-abc PATH [--out FILE]\n hexa parse-abc PATH\n hexa generate-yaml PATH [--out FILE]\n hexa check PATH_A PATH_B",
|
||||
"members": {
|
||||
"check_matches": {
|
||||
"name": "check_matches",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.check_matches",
|
||||
"signature": "check_matches(path_a: str | Path, path_b: str | Path)",
|
||||
"docstring": "Verify two representations agree.\n\nArgs:\n path_a (str | Path):\n Path to a ``.yaml`` or ``.py`` file.\n\n path_b (str | Path):\n Path to a ``.yaml`` or ``.py`` file.\n\nReturns:\n bool | list[str]:\n ``True`` if they match exactly, otherwise a list of difference\n strings."
|
||||
},
|
||||
"generate_abc": {
|
||||
"name": "generate_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.generate_abc",
|
||||
"signature": "generate_abc(node: PortNode, out_path: str | Path | None = None)",
|
||||
"docstring": "Generate ABC Python source from a :class:`PortNode` tree.\n\nArgs:\n node (PortNode):\n The root of the parsed YAML (or generated) tree.\n\n out_path (str | Path | None):\n Optional file path to write the generated source to.\n\nReturns:\n str:\n The generated Python source code."
|
||||
},
|
||||
"generate_yaml": {
|
||||
"name": "generate_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.generate_yaml",
|
||||
"signature": "generate_yaml(node: PortNode, out_path: str | Path | None = None)",
|
||||
"docstring": "Generate YAML source from a :class:`PortNode` tree.\n\nArgs:\n node (PortNode):\n The root of the tree.\n\n out_path (str | Path | None):\n Optional file path to write the generated source to.\n\nReturns:\n str:\n The generated YAML source."
|
||||
},
|
||||
"parse_abc": {
|
||||
"name": "parse_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.parse_abc",
|
||||
"signature": "parse_abc(module_or_path: str | Path | Any)",
|
||||
"docstring": "Parse an ABC Python module into a :class:`PortNode` tree.\n\nABC classes become port nodes. A nested-struct attribute is expressed as a\n``@dataclass`` annotation on an ABC port (e.g. ``source: ExtractionSource``\nwhere ``ExtractionSource`` is a ``@dataclass``); the dataclass's fields are\nexpanded into a nested ``attributes`` entry.\n\nArgs:\n module_or_path (str | Path | Any):\n A file path to a ``.py`` file, a module import string, or a loaded\n module.\n\nReturns:\n PortNode:\n The root node of the tree."
|
||||
},
|
||||
"parse_yaml": {
|
||||
"name": "parse_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.parse_yaml",
|
||||
"signature": "parse_yaml(path: str | Path)",
|
||||
"docstring": "Parse a hexa YAML spec file into a :class:`PortNode` tree.\n\nArgs:\n path (str | Path):\n Path to the ``.yaml`` file.\n\nReturns:\n PortNode:\n The root node of the parsed tree.\n\nRaises:\n ValueError:\n On structural errors in the YAML."
|
||||
},
|
||||
"main": {
|
||||
"name": "main",
|
||||
"kind": "function",
|
||||
"path": "hexa.cli.main",
|
||||
"signature": "main() -> None",
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"container": {
|
||||
"name": "container",
|
||||
"kind": "module",
|
||||
"path": "hexa.container",
|
||||
"signature": null,
|
||||
"docstring": "# Summary\n\nContainer to build a wired hexa pipeline from annotations.\n\n``build`` recursively instantiates the port tree implied by a root class's\nannotations, injecting provided runtime instances and config values by name.\n\n---\n# Notes\n\n- Runtime instances and config values are applied by name across the whole tree.",
|
||||
"members": {
|
||||
"T": {
|
||||
"name": "T",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.container.T",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"build": {
|
||||
"name": "build",
|
||||
"kind": "function",
|
||||
"path": "hexa.container.build",
|
||||
"signature": "build(cls: type[T], *, instances: dict[str, Any] | None = None, config: dict[str, Any] | None = None) -> T",
|
||||
"docstring": "Recursively instantiate the port tree from annotations.\n\nArgs:\n cls (type[T]):\n The root class to build (e.g. ``AxisExtractionPipeline``).\n\n instances (dict[str, Any] | None):\n ``{slot_name: object}`` injected verbatim into every node whose\n annotations contain that name (runtime values — repos, handlers,\n clients — that must not be re-constructed). Applied to the whole\n tree by name.\n\n config (dict[str, Any] | None):\n ``{field_name: value}`` propagated by name to every node's\n annotated config fields (non-port attributes), replacing the\n default ``None``.\n\nReturns:\n T:\n A fully wired instance of ``cls`` and all its nested ports."
|
||||
}
|
||||
}
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"kind": "module",
|
||||
"path": "hexa.model",
|
||||
"signature": null,
|
||||
"docstring": "# Summary\n\nShared neutral model for hexa port trees.\n\nAll utilities (``parse_yaml``, ``generate_abc``, ``parse_abc``,\n``generate_yaml``, ``check_matches``) serialize through this single model.\n\n---\n# Notes\n\n- The model is NOT tied to any sample — any port tree fits.",
|
||||
"members": {
|
||||
"NestedConfig": {
|
||||
"name": "NestedConfig",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.NestedConfig",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"ConfigValue": {
|
||||
"name": "ConfigValue",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.ConfigValue",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.model.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.model.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.model.PortNode.find",
|
||||
"signature": "find(name: str) -> PortNode | None",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.model.PortNode.walk",
|
||||
"signature": "walk() -> list[PortNode]",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.model.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '') -> list[str]",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
mcp/hexa/modules/hexa.model.json
Normal file
125
mcp/hexa/modules/hexa.model.json
Normal file
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"module": "hexa.model",
|
||||
"content": {
|
||||
"path": "hexa.model",
|
||||
"docstring": "# Summary\n\nShared neutral model for hexa port trees.\n\nAll utilities (``parse_yaml``, ``generate_abc``, ``parse_abc``,\n``generate_yaml``, ``check_matches``) serialize through this single model.\n\n---\n# Notes\n\n- The model is NOT tied to any sample — any port tree fits.",
|
||||
"objects": {
|
||||
"NestedConfig": {
|
||||
"name": "NestedConfig",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.NestedConfig",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"ConfigValue": {
|
||||
"name": "ConfigValue",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.ConfigValue",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.model.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.model.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.model.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.model.PortNode.find",
|
||||
"signature": "find(name: str) -> PortNode | None",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.model.PortNode.walk",
|
||||
"signature": "walk() -> list[PortNode]",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.model.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '') -> list[str]",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
mcp/hexa/modules/hexa.parse_abc.json
Normal file
125
mcp/hexa/modules/hexa.parse_abc.json
Normal file
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"module": "hexa.parse_abc",
|
||||
"content": {
|
||||
"path": "hexa.parse_abc",
|
||||
"docstring": "# Summary\n\nParse an ABC Python module into a hexa PortNode tree.\n\nABC classes become port nodes. A nested-struct attribute is expressed as a\n``@dataclass`` annotation on an ABC port (e.g. ``source: ExtractionSource``\nwhere ``ExtractionSource`` is a ``@dataclass``); the dataclass's fields are\nexpanded into a nested ``attributes`` entry.\n\n---\n# Notes\n\n- Accepts a file path, a module import string, or an already-loaded module.",
|
||||
"objects": {
|
||||
"ConfigValue": {
|
||||
"name": "ConfigValue",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.ConfigValue",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.parse_abc.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.parse_abc.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_abc.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_abc.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_abc.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_abc.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse_abc": {
|
||||
"name": "parse_abc",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_abc.parse_abc",
|
||||
"signature": "parse_abc(module_or_path: str | Path | Any) -> PortNode",
|
||||
"docstring": "Parse an ABC Python module into a :class:`PortNode` tree.\n\nABC classes become port nodes. A nested-struct attribute is expressed as a\n``@dataclass`` annotation on an ABC port (e.g. ``source: ExtractionSource``\nwhere ``ExtractionSource`` is a ``@dataclass``); the dataclass's fields are\nexpanded into a nested ``attributes`` entry.\n\nArgs:\n module_or_path (str | Path | Any):\n A file path to a ``.py`` file, a module import string, or a loaded\n module.\n\nReturns:\n PortNode:\n The root node of the tree."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
mcp/hexa/modules/hexa.parse_yaml.json
Normal file
125
mcp/hexa/modules/hexa.parse_yaml.json
Normal file
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"module": "hexa.parse_yaml",
|
||||
"content": {
|
||||
"path": "hexa.parse_yaml",
|
||||
"docstring": "# Summary\n\nParse a hexa YAML spec into a :class:`PortNode` tree.\n\nThe YAML grammar uses a nested mapping format built from three member kinds:\n\n- **attribute**: a ``name: type`` scalar (e.g. ``source_type: str``)\n- **nested struct**: a ``name:`` mapping of sub-attributes (e.g. ``source:``)\n- **port**: a ``name:`` mapping whose single key is a class whose value is a\n body, or a class key with a ``...`` body. A port carries a class shape.\n\nDisambiguation of a mapping value:\n A mapping with exactly one key whose value is a non-scalar body (a mapping,\n ``...``, or a list) is a **port**. Any other mapping (multiple keys, or all\n scalar-typed values) is a **nested struct** of attributes.\n\n---\n# Examples\n\nParse a spec into a port tree:\n\n ```python\n from hexa import parse_yaml\n\n node = parse_yaml(\"samples/minimal/sample.yaml\")\n print(node)\n ```\n\nThe YAML grammar:\n\n ```yaml\n RootClassName:\n attr: type # scalar attribute\n source: # nested struct (all scalar sub-attributes)\n source_type: str\n bank: str\n methods: # reserved block of functions\n run:\n args: [a, b]\n ingest: # port (single non-scalar class key)\n IngestPort:\n source_type: str\n methods: {Method: ...}\n ```\n\n---\n# Notes\n\n- See ``samples/minimal/sample.yaml`` and\n ``samples/extraction_pipeline/extraction_pipeline.yaml`` for worked examples.",
|
||||
"objects": {
|
||||
"ConfigValue": {
|
||||
"name": "ConfigValue",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.ConfigValue",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"MethodSpec": {
|
||||
"name": "MethodSpec",
|
||||
"kind": "class",
|
||||
"path": "hexa.parse_yaml.MethodSpec",
|
||||
"signature": "MethodSpec(name: str, args: list[str] = list(), kwargs: dict[str, str] = dict())",
|
||||
"docstring": "Specification of a single abstract method.\n\nAttributes:\n name (str):\n Name of the method.\n\n args (list[str]):\n Positional parameter names.\n\n kwargs (dict[str, str]):\n Keyword-only parameter names mapped to their type expressions.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.MethodSpec.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"args": {
|
||||
"name": "args",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.MethodSpec.args",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kwargs": {
|
||||
"name": "kwargs",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.MethodSpec.kwargs",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"PortNode": {
|
||||
"name": "PortNode",
|
||||
"kind": "class",
|
||||
"path": "hexa.parse_yaml.PortNode",
|
||||
"signature": "PortNode(name: str, port_cls: str, kind: str = 'leaf', attributes: dict[str, ConfigValue] = dict(), children: list[PortNode] = list(), methods: dict[str, MethodSpec] = dict())",
|
||||
"docstring": "A node in the hexa port tree.\n\nEach node is a leaf (no children), a port (has nested port slots), or the\nparent root of the tree. Every node carries a concrete typed shape derived\nfrom an ABC class name.\n\nAttributes:\n name (str):\n Slot name, e.g. ``\"parser\"``, ``\"amount_balance\"``.\n\n port_cls (str):\n ABC class name that gives this port its shape, e.g.\n ``\"TransactionParserPort\"``. Every port has a concrete typed shape.\n\n kind (str):\n One of ``\"leaf\"``, ``\"port\"``, or ``\"parent\"``.\n - ``\"leaf\"``: no children (e.g. ``NumberPort``)\n - ``\"port\"``: has children (e.g. ``TransactionParserPort``)\n - ``\"parent\"``: the root of the tree (e.g. ``ExtractionPipeline``)\n\n attributes (dict[str, ConfigValue]):\n Annotated attributes: ``name -> type_expr`` for scalars, or a\n nested ``dict`` of sub-attributes (a struct) for grouped values.\n\n children (list[PortNode]):\n Nested port slots.\n\n methods (dict[str, MethodSpec]):\n Abstract methods keyed by method name.",
|
||||
"members": {
|
||||
"name": {
|
||||
"name": "name",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.name",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"port_cls": {
|
||||
"name": "port_cls",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.port_cls",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"kind": {
|
||||
"name": "kind",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.kind",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"attributes": {
|
||||
"name": "attributes",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.attributes",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.children",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"methods": {
|
||||
"name": "methods",
|
||||
"kind": "attribute",
|
||||
"path": "hexa.parse_yaml.PortNode.methods",
|
||||
"signature": null,
|
||||
"docstring": null
|
||||
},
|
||||
"find": {
|
||||
"name": "find",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_yaml.PortNode.find",
|
||||
"signature": "find(name: str)",
|
||||
"docstring": "Find a direct child by slot name.\n\nArgs:\n name (str):\n Slot name of the child to look up.\n\nReturns:\n PortNode | None:\n The matching child node, or ``None`` if no child has that name."
|
||||
},
|
||||
"walk": {
|
||||
"name": "walk",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_yaml.PortNode.walk",
|
||||
"signature": "walk()",
|
||||
"docstring": "Return all nodes in depth-first order (self first).\n\nReturns:\n list[PortNode]:\n All nodes of the tree, self first, depth-first."
|
||||
},
|
||||
"diff": {
|
||||
"name": "diff",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_yaml.PortNode.diff",
|
||||
"signature": "diff(other: PortNode, path: str = '')",
|
||||
"docstring": "Return a list of human-readable difference strings.\n\nAn empty list means the trees are equal.\n\nArgs:\n other (PortNode):\n Tree to compare against.\n\n path (str):\n Path prefix used when recursing into nested nodes; defaults to\n the root path.\n\nReturns:\n list[str]:\n Human-readable difference strings, one per discrepancy."
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse_yaml": {
|
||||
"name": "parse_yaml",
|
||||
"kind": "function",
|
||||
"path": "hexa.parse_yaml.parse_yaml",
|
||||
"signature": "parse_yaml(path: str | Path) -> PortNode",
|
||||
"docstring": "Parse a hexa YAML spec file into a :class:`PortNode` tree.\n\nArgs:\n path (str | Path):\n Path to the ``.yaml`` file.\n\nReturns:\n PortNode:\n The root node of the parsed tree.\n\nRaises:\n ValueError:\n On structural errors in the YAML."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
mcp/hexa/nav.json
Normal file
38
mcp/hexa/nav.json
Normal file
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"module": "hexa",
|
||||
"resource": "docs://modules/hexa"
|
||||
},
|
||||
{
|
||||
"module": "hexa.check_matches",
|
||||
"resource": "docs://modules/hexa.check_matches"
|
||||
},
|
||||
{
|
||||
"module": "hexa.cli",
|
||||
"resource": "docs://modules/hexa.cli"
|
||||
},
|
||||
{
|
||||
"module": "hexa.container",
|
||||
"resource": "docs://modules/hexa.container"
|
||||
},
|
||||
{
|
||||
"module": "hexa.generate_abc",
|
||||
"resource": "docs://modules/hexa.generate_abc"
|
||||
},
|
||||
{
|
||||
"module": "hexa.generate_yaml",
|
||||
"resource": "docs://modules/hexa.generate_yaml"
|
||||
},
|
||||
{
|
||||
"module": "hexa.model",
|
||||
"resource": "docs://modules/hexa.model"
|
||||
},
|
||||
{
|
||||
"module": "hexa.parse_abc",
|
||||
"resource": "docs://modules/hexa.parse_abc"
|
||||
},
|
||||
{
|
||||
"module": "hexa.parse_yaml",
|
||||
"resource": "docs://modules/hexa.parse_yaml"
|
||||
}
|
||||
]
|
||||
6
mcp/standards/index.json
Normal file
6
mcp/standards/index.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"project": "standards",
|
||||
"type": "standards",
|
||||
"modules_count": 14,
|
||||
"source": "hand-authored"
|
||||
}
|
||||
78
mcp/standards/libraries.json
Normal file
78
mcp/standards/libraries.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"project": "standards",
|
||||
"note": "Machine-readable library registry. Mirrors standards_src/wiki/13_library_registry.md. Query with the find_library MCP tool.",
|
||||
"libraries": [
|
||||
{
|
||||
"name": "DAG Pipe",
|
||||
"import": "dagpipe",
|
||||
"purpose": "Deterministic state-transformation DAG engine.",
|
||||
"use_instead_of": "hand-rolled orchestration, topological ordering, immutable state propagation",
|
||||
"capabilities": ["pipeline", "orchestration", "dag", "graph", "engine", "state", "node"],
|
||||
"mcp": { "server": "dagpipe", "port": 8006 }
|
||||
},
|
||||
{
|
||||
"name": "Hexa",
|
||||
"import": "hexa",
|
||||
"purpose": "Declarative dependency injection and composition-root tooling.",
|
||||
"use_instead_of": "manual wiring, hand-written factories, manual composition roots",
|
||||
"capabilities": ["di", "dependency-injection", "composition", "wiring", "ports"],
|
||||
"mcp": { "server": "hexa", "port": null, "note": "bundle exists; port not yet assigned" }
|
||||
},
|
||||
{
|
||||
"name": "Mongo Ops",
|
||||
"import": "mongo_ops",
|
||||
"purpose": "Async MongoDB repositories, caching, population and transactions.",
|
||||
"use_instead_of": "raw Motor CRUD, connection lifecycle, generic repository code",
|
||||
"capabilities": ["mongo", "mongodb", "database", "repository", "crud", "cache", "transactions"],
|
||||
"mcp": { "server": "mongo_ops", "port": 8007 }
|
||||
},
|
||||
{
|
||||
"name": "Omniread",
|
||||
"import": "omniread",
|
||||
"purpose": "Format-agnostic content acquisition and parsing (HTML, PDF, CSV, XLSX).",
|
||||
"use_instead_of": "custom scrapers and parsers for HTML/PDF/CSV/XLSX",
|
||||
"capabilities": ["ingestion", "parse", "pdf", "csv", "xlsx", "html", "scrape", "fetch"],
|
||||
"mcp": { "server": "omniread", "port": 8003 }
|
||||
},
|
||||
{
|
||||
"name": "OpenAPI First",
|
||||
"import": "openapi_first",
|
||||
"purpose": "Spec-first FastAPI bootstrap, client, and codegen.",
|
||||
"use_instead_of": "decorator routers and hand-written API clients",
|
||||
"capabilities": ["openapi", "http", "api", "routing", "fastapi", "client", "codegen", "spec"],
|
||||
"mcp": { "server": "openapi_first", "port": 8004 }
|
||||
},
|
||||
{
|
||||
"name": "Mail Intake",
|
||||
"import": "mail_intake",
|
||||
"purpose": "Provider-agnostic read-only email ingestion.",
|
||||
"use_instead_of": "Gmail OAuth and MIME parsing implemented by hand",
|
||||
"capabilities": ["email", "mail", "gmail", "ingestion", "imap", "mime", "oauth"],
|
||||
"mcp": { "server": "mail_intake", "port": 8002 }
|
||||
},
|
||||
{
|
||||
"name": "Py JWT",
|
||||
"import": "jwtlib",
|
||||
"purpose": "JWT authentication primitives (register, login, introspect).",
|
||||
"use_instead_of": "password hashing and token issuance/verification written by hand",
|
||||
"capabilities": ["auth", "jwt", "token", "login", "password", "introspect", "security"],
|
||||
"mcp": { "server": "jwtlib", "port": 8005 }
|
||||
},
|
||||
{
|
||||
"name": "Doc Forge",
|
||||
"import": "docforge",
|
||||
"purpose": "Docstring compiler that renders documentation for humans (MkDocs) and machines (MCP).",
|
||||
"use_instead_of": "custom documentation generators",
|
||||
"capabilities": ["docs", "documentation", "mkdocs", "mcp", "docstrings", "gsdfc"],
|
||||
"mcp": { "server": "docforge", "port": 8001 }
|
||||
},
|
||||
{
|
||||
"name": "Auth Server",
|
||||
"import": "auth-server",
|
||||
"purpose": "Central identity service (register/login/me/introspect).",
|
||||
"use_instead_of": "building authentication endpoints yourself",
|
||||
"capabilities": ["auth", "identity", "service", "register", "login", "introspect"],
|
||||
"mcp": { "server": null, "port": null, "note": "service; no MCP bundle" }
|
||||
}
|
||||
]
|
||||
}
|
||||
8
mcp/standards/modules/build_recipe.json
Normal file
8
mcp/standards/modules/build_recipe.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "build_recipe",
|
||||
"content": {
|
||||
"path": "build_recipe",
|
||||
"docstring": "# Build Recipe\n\n## Summary\n\nFollow this ordered checklist to generate a new application of the standard\nshape. Every step is deterministic; only steps 2 and 7 require app-specific\nauthoring.\n\n---\n\n## 0. Query the registry\n\nBefore writing anything, read the **library registry** and note which internal\nlibraries provide DB, DI, routing, ingestion, auth, and docs. Do not\nreimplement them.\n\n## 1. Scaffold the repository\n\nCopy the `templates/` skeleton. It provides: `pyproject.toml`, `openapi.yaml`,\n`main.py`, `init.py`, `worker.py`, `config.py`, `Dockerfile`,\n`docker-compose.yaml`, `.env.example`, and the package tree\n(`domain/ infra/ interfaces/ pipelines/ tests/ scripts/`).\n\n## 2. Author the spec *(app-specific)*\n\nWrite `openapi.yaml`: resources, schemas, `operationId`s, security, and any\nextension metadata. This is the contract.\n\n## 3. Write each CRUD slice *(app-specific)*\n\nFor each resource, create the four-file slice (see **CRUD Template**):\n`models.py`, `ports/db.py`, `ports/service.py`, optional `converters.py`.\n\n## 4. Wire the composition root\n\nIn `init.py`: build leaf repositories, then dependent repositories, then\nservices; add infrastructure adaptors (cache store, progress bus, stores,\nfetchers). Add `get_svc` overloads.\n\n## 5. Add routes\n\nCreate `interfaces/<resource>/routes.py` (thin handlers) and re-export them in\n`interfaces/openapi.py`. Add a cache decorator to read endpoints.\n\n## 6. Bootstrap the app and worker\n\nConfirm `main.py` wires the OpenAPI-first app with the lifespan. If the app has\nlong-running jobs, adapt `worker.py` to poll the relevant job collection.\n\n## 7. Add a pipeline family *(only if data-driven)*\n\nCreate `pipelines/<family>/` with `abc.py`, `impl.py`, `pipeline.yaml`,\n`resources/`, and register the root in `registry.py`. Add fixtures and a parity\ntest.\n\n## 8. Add infrastructure as needed\n\nOnly where the standard adaptors do not cover the need. Prefer library-backed\nadapters.\n\n## 9. Tests\n\nAdd integration tests with fakes and unit tests for domain/pipeline logic; add\na config-consistency test if using rule-driven pipelines.\n\n## 10. Scripts and seeds\n\nAdd operator scripts under `scripts/` that use `init()` / `get_svc()` /\n`shutdown()`.\n\n## 11. Docs\n\nAuthor GSDFC docstrings, generate the type stubs (`.pyi`), mark the package\n`py.typed`, and build docs.\n\n## Definition of done\n\n- `python -c \"import main\"` succeeds.\n- `pytest` passes.\n- The app serves the spec's operations; the worker drains its queue.\n- No domain module imports a web framework.\n- No infrastructure was reimplemented that a library already provides.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/composition_root.json
Normal file
8
mcp/standards/modules/composition_root.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "composition_root",
|
||||
"content": {
|
||||
"path": "composition_root",
|
||||
"docstring": "# Composition Root\n\n## Summary\n\nAll wiring happens in exactly one module. It builds a **flat resource\nregistry** and exposes it to the HTTP layer and to scripts.\n\n---\n\n## The resource registry\n\n`init.py` returns a plain `dict[str, Any]`. Keys follow two conventions:\n\n- `<resource>_repo` — a repository adapter for a resource.\n- `<resource>_svc` — a service for a resource.\n\nPlus named infrastructure adaptors:\n\n- `http_cache_store` — response cache backend.\n- `progress_bus` — progress/event bus.\n- `<vendor>_<kind>` — fetchers, clients, stores.\n\nThe API stores the dict on `app.state.resources` during lifespan startup.\nHandlers read from it; nothing imports concrete adapters.\n\n## Shape\n\n```python\n_resources: dict = {}\n_initialized = False\n\nasync def init(settings=None) -> dict:\n global _initialized\n if _initialized:\n return _resources\n\n await setup_database(uri=settings.mongo_uri, db_name=settings.mongo_db)\n\n # 1. leaf repositories\n user_repo = create_user_repo()\n\n # 2. dependent repositories (dependencies passed by keyword)\n wallet_repo = create_wallet_repo(user_repo=user_repo)\n\n # 3. services (build via the DI library, injecting the repo)\n _resources[\"user_repo\"] = user_repo\n _resources[\"wallet_repo\"] = wallet_repo\n _resources[\"user_svc\"] = get_user_service(user_repo)\n _resources[\"wallet_svc\"] = get_wallet_service(wallet_repo)\n\n # 4. infrastructure adaptors (optional)\n _resources[\"http_cache_store\"] = HttpCacheStore(...)\n _resources[\"progress_bus\"] = ProgressEventBus()\n\n _initialized = True\n return _resources\n\nasync def shutdown() -> None:\n global _initialized\n await close_stores(_resources)\n await shutdown_database()\n _resources.clear()\n _initialized = False\n```\n\n## Typed accessors\n\nExpose a `get_svc(name)` helper with one `@overload` per key so scripts get\ntyping without importing concrete classes:\n\n```python\n@overload\ndef get_svc(name: Literal[\"user_svc\"]) -> UserService: ...\n@overload\ndef get_svc(name: Literal[\"wallet_svc\"]) -> WalletService: ...\n\ndef get_svc(name: str):\n return _resources[name]\n```\n\nAlso expose `get_db()` (raw database handle) and pure helpers such as\n`compute_fingerprint(payload)` (canonical hash used for deduplication).\n\n## Rules\n\n- `init()` is **idempotent**.\n- The composition root is the only place that imports concrete adapters.\n- Handlers never call `init()`; they read `request.app.state.resources`.\n- Scripts call `init()` / `shutdown()` around their work.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/crud_template.json
Normal file
8
mcp/standards/modules/crud_template.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "crud_template",
|
||||
"content": {
|
||||
"path": "crud_template",
|
||||
"docstring": "# CRUD Template\n\n## Summary\n\nEvery resource follows the same four-file slice. Repetition is intentional: it\nmakes the codebase predictable and generatable.\n\n---\n\n## The slice\n\n```\ndomain/<resource>/\n models.py # pure Pydantic model (no DB fields)\n ports/\n db.py # document model + repository factory\n service.py # service class + factory\n converters.py # (optional) transforms\n```\n\n## models.py\n\nA framework-free Pydantic model describing the business shape.\n\n```python\nclass Item(BaseModel):\n name: str\n quantity: int = 0\n```\n\n## ports/db.py\n\nThe persistence model extends the business model and adds database concerns.\nThe repository factory selects the right repository flavour.\n\n```python\nclass ItemDocument(Item, BaseDocument): # BaseDocument adds id/created_at/...\n pass\n\ndef create_item_repo(*, owner_repo=None) -> ItemRepository:\n return repo_factory(\n collection=\"items\",\n document=ItemDocument,\n populate_rules=[PopulateRule(field=\"owner\", collection=\"users\")],\n cache=None, # or a CachedBaseRepository config\n )\n```\n\n## ports/service.py\n\nThe service is built through the DI library, injecting the repository. The\ngeneric CRUD base provides `create/read/update/patch/delete/search/...`.\n\n```python\nclass ItemService(CrudsServiceAdaptor[Item, ItemDocument]):\n model = Item\n schema = ItemDocument\n\ndef get_item_service(repo) -> ItemService:\n return build(ItemService, instances={\"repo\": repo})\n```\n\n## Naming conventions\n\n| Concept | Convention |\n|---|---|\n| collection | plural snake case (`items`) |\n| document | `<Resource>Document` |\n| repository factory | `create_<resource>_repo` |\n| service | `<Resource>Service` |\n| service factory | `get_<resource>_service` |\n| resource key | `<resource>_svc` |\n| operationId | `list_items`, `create_item`, ... |\n\n## Optional files\n\n- `converters.py` — pure functions mapping between domain types.\n- `ports/enrichment.py` — adapts a pipeline into an async callable the service\n can invoke (used when a resource is populated by a pipeline).\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/genericity_matrix.json
Normal file
8
mcp/standards/modules/genericity_matrix.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "genericity_matrix",
|
||||
"content": {
|
||||
"path": "genericity_matrix",
|
||||
"docstring": "# Genericity Matrix\n\n## Summary\n\nThis page states precisely what is **reusable as-is**, what is a **template to\nadapt**, and what is **app-specific and must be authored**. Use it to scope a\nnew build.\n\n---\n\n## Reusable as-is (do not rewrite)\n\n| Area | Artifact |\n|---|---|\n| Layering & dependency rule | the package layout and import rules |\n| Composition root | the `init.py` shape, idempotency, `get_svc` overloading |\n| Web edge | `OpenAPIFirstApp` bootstrap, handler-name = `operationId`, thin handlers |\n| CRUD slice | the four-file pattern (model / ports-db / ports-service / converters) |\n| Generic CRUD service | the base service adaptor |\n| Repository factory | base / populating / cached selection |\n| Worker | the poll loop, backoff, retry, status machine |\n| Progress bus | the bus port + in-memory adapter + streaming endpoint |\n| Human-in-the-loop | the durable pause + question document + resolve endpoint + resume map |\n| Infra adaptors | HTTP cache decorator, KV store port, LLM pool scaffold |\n| Testing | integration harness with fakes, fixture-parity pattern |\n| Docs | GSDFC + generated stubs + MCP bundle pipeline |\n\n## Template to adapt (copy, then change names)\n\n| Artifact | Change |\n|---|---|\n| `pyproject.toml` | project name, dependencies |\n| `Dockerfile` / `docker-compose.yaml` | service name, ports, services |\n| `.env.example` | connection strings, provider keys |\n| `openapi.yaml` | every path/schema/operationId |\n| `scripts/` | seed and operator tasks |\n\n## App-specific (must be authored)\n\n| Artifact | Why |\n|---|---|\n| Resource models | the business shape |\n| Repository populate/cache rules | the relationships |\n| Service business logic | domain behaviour |\n| Pipeline families (if any) | the transformation graph |\n| Pattern/rule resources | data-driven policy |\n| Fixtures & golden outputs | test data |\n\n## The two axes\n\nEverything app-specific reduces to **two axes**:\n\n1. **Resource model** — the set of resources, their fields, relationships, and\n the OpenAPI contract.\n2. **Pipeline family** — only if inputs require multi-step transformation.\n\nIf you can specify those two axes, the rest of the app follows mechanically.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/human_in_loop.json
Normal file
8
mcp/standards/modules/human_in_loop.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "human_in_loop",
|
||||
"content": {
|
||||
"path": "human_in_loop",
|
||||
"docstring": "# Human-in-the-Loop\n\n## Summary\n\nSome inputs are ambiguous and must be resolved by a human before processing can\ncontinue. The blueprint models this as a **durable pause**: an exception is\nraised, a question is recorded, the job pauses, and later resumes.\n\n---\n\n## The mechanism\n\n1. During processing, a stage raises a domain exception carrying the context\n needed to ask the question:\n\n ```python\n raise PauseForResolution(\n job_id=jid,\n reference=\"<stable key for this decision>\",\n options=[...],\n context={...},\n )\n ```\n\n2. The orchestrator catches it, records a **question document**, and re-raises\n so the worker marks the job `paused`.\n3. A client lists pending questions and answers one:\n\n ```\n GET /jobs/{id}/questions -> list pending questions\n POST /questions/{qid}/resolve {choice} -> record the choice\n ```\n\n4. When the **last** pending question is resolved, the resolve endpoint flips\n the parent job back to `pending`.\n5. The worker picks it up again, rebuilds a **resolution map** from previously\n answered questions, and seeds it into the pipeline so the same decision is\n not asked twice.\n\n## The question document\n\n```python\nclass Question(BaseDocument):\n job_id: str\n reference: str # stable key: stage|field|value\n status: str # pending | resolved\n options: list[dict]\n context: dict\n chosen: dict | None = None\n resolved_at: str | None = None\n```\n\n## Resume map\n\nThe worker turns answered questions into a lookup keyed by `reference`, then\ninjects it into the pipeline so that stage can skip re-raising:\n\n```python\nresolved = {q.reference: q.chosen for q in await svc.search(\n filter={\"job_id\": jid, \"status\": \"resolved\"})}\n\npipeline = build(root, instances={\"pause\": handler}, config={\"resolved\": resolved})\n```\n\n## Resolve endpoint\n\n```python\nasync def resolve_question(request, id, body):\n svc = request.app.state.resources[\"question_svc\"]\n q = await svc.read(id)\n await svc.update(id, {\"chosen\": body[\"chosen\"], \"status\": \"resolved\",\n \"resolved_at\": now()})\n\n pending = await svc.search(filter={\"job_id\": q.job_id, \"status\": \"pending\"},\n limit=1)\n if not pending:\n jobs = request.app.state.resources[\"job_svc\"]\n job = await jobs.read(q.job_id)\n if job and job.status == \"paused\":\n await jobs.update_status(q.job_id, \"pending\")\n return q\n```\n\n## CLI parity\n\nThe same flow is available through scripts/CLI: `list`, `get`, `create`,\n`questions`, `resolve`, `retry`.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/index.json
Normal file
8
mcp/standards/modules/index.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "index",
|
||||
"content": {
|
||||
"path": "index",
|
||||
"docstring": "# The Aetos Standard App Blueprint\n\nA single, reusable architecture for backend applications in the Aetos ecosystem.\n\nEvery application built with this blueprint has the same shape:\n\n- a **spec-first HTTP layer** driven by an OpenAPI document;\n- a **domain layer** of pure models, ports, and services;\n- an **infrastructure layer** of adapters (MongoDB, cache, workers, LLM, mail);\n- a **composition root** that wires everything in exactly one place;\n- an optional **pipeline layer** for data-driven extraction/enrichment;\n- an optional **background worker** that runs long-running jobs and reports progress.\n\n> This blueprints is deliberately **app-agnostic**. It never names a concrete\n> business domain. Any application — expenses, health metrics, inventory,\n> analytics — is produced by filling in **two axes only**: the resource model\n> and (if needed) the pipeline family.\n\n## How to read this\n\n| Page | What it covers |\n|---|---|\n| Overview | The philosophy and the one-way dependency rule |\n| Layering Rules | Which package may import which |\n| Composition Root | The single wiring point |\n| Spec-First Routing | OpenAPI as the source of truth |\n| CRUD Template | The canonical resource slice |\n| Pipeline Scaffold | Data-driven processing families |\n| Worker & Progress | Async jobs, status machine, streaming progress |\n| Human-in-the-Loop | Pausing a job to ask a question |\n| Infra Adapters | MongoDB, cache, stores, LLM, mail |\n| Testing | Integration + unit conventions |\n| Build Recipe | The ordered checklist to generate a new app |\n| Genericity Matrix | What is reusable vs what you must write |\n\n## For AI agents\n\nThis content is served as MCP resources under `docs://index`, `docs://nav`,\nand `docs://modules/{module}`. To build a new app:\n\n1. Read `build_recipe` first.\n2. Read `genericity_matrix` to know what you must author.\n3. Query the **library registry** (`find_library`) before writing any\n infrastructure yourself.\n4. Copy the `templates/` skeleton and fill the two app-specific axes.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/infra_adaptors.json
Normal file
8
mcp/standards/modules/infra_adaptors.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "infra_adaptors",
|
||||
"content": {
|
||||
"path": "infra_adaptors",
|
||||
"docstring": "# Infra Adapters\n\n## Summary\n\nAll external systems are reached through adapters in `infra/`. Prefer an\nexisting internal library over writing a new adapter.\n\n---\n\n## Database (MongoDB)\n\n- A single connection manager owns the client lifecycle.\n `setup_database(uri, db_name)` / `shutdown_database()`.\n- A repository **factory** selects the repository flavour:\n\n ```python\n repo_factory(collection, document, engine=None,\n populate_rules=None, cache=None)\n ```\n\n - base repository — plain CRUD;\n - populating repository — resolves referenced documents (foreign keys);\n - cached repository — an LRU/TTL cache in front of reads.\n\n- Referenced documents are described declaratively:\n\n ```python\n PopulateRule(field=\"owner\", collection=\"users\",\n nested_rules=[], max_depth=2, projection=None)\n ```\n\n- Transactions use a session context manager provided by the library.\n\n**Library:** the Mongo operations library (see registry). Do not write raw\nMotor calls in the domain.\n\n## HTTP response cache\n\nA generic GET cache sits behind a decorator.\n\n```python\n@cache_response(ttl=60)\nasync def list_items(request): ...\n```\n\n- Keyed on path + query + user.\n- No-op when no store is wired.\n- `invalidate(*resources)` bumps a generation counter for write-triggered\n invalidation.\n\n## Event / progress bus\n\nA port with an in-memory default adapter (`publish`, `get_events`). Swap for a\ndurable broker in production (see Worker & Progress).\n\n## External clients (ingestion)\n\nExternal fetchers (files, mail, HTTP) are adapters that wrap ingestion\nlibraries. Construct them in the composition root and inject into\norchestrators.\n\n**Libraries:** the ingestion library (formats) and the mail library (email).\n\n## LLM clients\n\nWhen a pipeline calls a model provider, route through a pool adaptor that\nhandles provider selection, retries, and client lifecycle:\n\n```python\npool = setup_async_pool(provider=settings.llm_provider)\nresult = await pool.complete(model=\"...\", messages=[...])\n```\n\nProviders are selected by `LLM_PROVIDER` and configured by environment.\n\n## Key-value store\n\nFor report/snapshot storage, provide a pluggable store port with a Redis\nadapter and an in-memory fallback.\n\n## Rule\n\nIf a capability already exists as a library (DB, DI, ingestion, mail, auth,\ndocs), **use it**. Writing a private copy of a library is a defect.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/layering_rules.json
Normal file
8
mcp/standards/modules/layering_rules.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "layering_rules",
|
||||
"content": {
|
||||
"path": "layering_rules",
|
||||
"docstring": "# Layering Rules\n\n## Summary\n\nThe dependency direction is fixed. Violating it is a design defect.\n\n---\n\n## Allowed imports\n\n```\ninterfaces/* -> domain/* (services, ports, models)\ninterfaces/* -> infra/adapters/* (only read-only value objects)\ndomain/* -> domain/* (same layer)\ndomain/* -> pipelines/* (call a pipeline via a port)\ninfra/adapters/* -> domain/ports/* (implement the interface)\nmain.py/init.py -> everything (composition root only)\n```\n\n## Forbidden imports\n\n```\ndomain/* -> interfaces/* (never)\ndomain/* -> fastapi/starlette (never)\ninfra/adapters/* -> interfaces/* (never)\ninterfaces/* -> infra/db/* (state lives in resources, not imports)\n```\n\n## Why\n\n- A pure domain is testable without a server, a database, or an event loop.\n- Adapters can be swapped (in-memory fake, real database) without touching\n business logic.\n- The HTTP edge stays thin and replaceable.\n\n## The port/adapter contract\n\nA **port** is an abstract class or protocol declared in `domain/<x>/ports/`.\nAn **adapter** implements it in `infra/adapters/`. The composition root binds\nthe two.\n\n```\ndomain/<resource>/ports/db.py -> repository port + document model\ndomain/<resource>/ports/service.py -> service + factory\ninfra/db/repos/<vendor>.py -> repository adapter factory\ninfra/adapters/service.py -> generic CRUD service base\n```\n\n## Enforcement\n\n- Keep every domain module importable with only the standard library and\n Pydantic. If a domain import pulls in `fastapi`, the layering is broken.\n- Reviewers (and CI) reject `import fastapi` inside `domain/` or `infra/`.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/library_registry.json
Normal file
8
mcp/standards/modules/library_registry.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "library_registry",
|
||||
"content": {
|
||||
"path": "library_registry",
|
||||
"docstring": "# Library Registry\n\n## Summary\n\nThe Aetos ecosystem provides internal libraries for the shared concerns of\nevery application. **Use them.** Writing a private copy of any capability below\nis a defect.\n\nEach library also exposes its own documentation as an MCP server. An AI agent\ncan read a library's full API by connecting to its MCP and reading\n`docs://index`, `docs://nav`, and `docs://modules/{module}`.\n\n---\n\n## Catalog\n\n| Library | Import | Purpose | Use instead of | MCP |\n|---|---|---|---|---|\n| DAG Pipe | `dagpipe` | Deterministic state-transformation DAG engine | hand-rolled orchestration/topological ordering | `:8006` |\n| Hexa | `hexa` | Declarative DI / composition-root tooling | manual wiring, hand-written factories | *(see note)* |\n| Mongo Ops | `mongo_ops` | Async MongoDB repositories, cache, populate, transactions | raw Motor CRUD and connection lifecycle | `:8007` |\n| Omniread | `omniread` | Format-agnostic fetch + parse (HTML, PDF, CSV, XLSX) | custom scrapers/parsers | `:8003` |\n| OpenAPI First | `openapi_first` | Spec-first FastAPI bootstrap, client, codegen | decorator routers and hand-written clients | `:8004` |\n| Mail Intake | `mail_intake` | Provider-agnostic read-only email ingestion | Gmail OAuth + MIME parsing | `:8002` |\n| Py JWT | `jwtlib` | JWT auth primitives (register/login/introspect) | password hashing + token code | `:8005` |\n| Doc Forge | `docforge` | Docstring compiler → MkDocs + MCP docs | custom doc generators | `:8001` |\n| Auth Server | *(service)* | Central identity service (register/login/me/introspect) | building auth endpoints yourself | — |\n\n## How to choose\n\n| You need to… | Use |\n|---|---|\n| persist and query documents | Mongo Ops |\n| compose dependencies | Hexa |\n| run a multi-step transformation | DAG Pipe |\n| read/parse an arbitrary input format | Omniread |\n| expose a contract-first HTTP API | OpenAPI First |\n| ingest email | Mail Intake |\n| authenticate users / verify tokens | Py JWT (+ Auth Server) |\n| generate documentation | Doc Forge |\n\n## Discovery\n\nA single **standards MCP** exposes `find_library(capability)`, which returns the\nmatching library, its import name, its \"use instead of\" guidance, and its MCP\nendpoint. Query it before writing any infrastructure.\n\n## Notes\n\n- **Hexa** ships an MCP bundle but is not yet assigned a dedicated port in the\n hub config; it is reachable via the standards registry entry and will be\n wired to a port.\n- Library MCP servers expose **documentation resources** (not executable tools):\n they tell an agent the available API surface so it does not invent code.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/overview.json
Normal file
8
mcp/standards/modules/overview.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "overview",
|
||||
"content": {
|
||||
"path": "overview",
|
||||
"docstring": "# Overview\n\n## Summary\n\nThe blueprint is a **hexagonal (ports and adapters) application** with a\n**spec-first** web edge and a **single composition root**. It favors explicit\nwiring over implicit magic, and it prefers existing internal libraries over\nhand-written infrastructure.\n\n---\n\n## The three layers\n\n```\ninterfaces/ ← HTTP edge: thin handlers bound to an OpenAPI operationId\ndomain/ ← business core: models, ports (interfaces), services, orchestrators\ninfra/ ← adapters: databases, caches, external clients, hardware\n```\n\nPlus two optional layers:\n\n```\npipelines/ ← data-driven transformation graphs (extraction, enrichment)\nscripts/ ← operator scripts that reuse the composition root\n```\n\n## Core principles\n\n1. **One-way dependencies.** `interfaces -> domain -> infra` is allowed;\n the reverse is forbidden. The domain never imports a framework it does not\n own.\n2. **Ports in the domain, adapters in infra.** A service depends on an\n abstract port; the composition root injects a concrete adapter.\n3. **One composition root.** Nothing constructs its own dependencies. All\n wiring lives in one module (`init.py`) and is exposed as a flat resource\n registry.\n4. **Spec-first.** The HTTP contract is declared in an OpenAPI document. Route\n handlers are looked up by `operationId`. There are no decorator routers.\n5. **Reuse the libraries.** Pipeline orchestration, DI, Mongo repositories,\n ingestion, auth, and docs are provided by internal libraries. Do not\n reimplement them — see the library registry.\n6. **Explicit state machines.** Long-running work moves through explicit,\n durable statuses and can pause for human input.\n\n## Runtime processes\n\nA standard app may run as up to three processes that share the same\ncomposition root:\n\n| Process | Entry point | Responsibility |\n|---|---|---|\n| API | `main.py` | serve HTTP; start/stop resources via lifespan |\n| Worker | `worker.py` | poll durable jobs, run pipelines, publish progress |\n| Scripts | `python -m scripts.<name>` | one-shot operator tasks |\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/pipeline_scaffold.json
Normal file
8
mcp/standards/modules/pipeline_scaffold.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "pipeline_scaffold",
|
||||
"content": {
|
||||
"path": "pipeline_scaffold",
|
||||
"docstring": "# Pipeline Scaffold\n\n## Summary\n\nData-driven processing is expressed as a **composition graph**. The graph is\nbuilt by a DI library; execution is driven by a DAG engine. Stages are pure and\nindividually testable.\n\n---\n\n## When to use a pipeline\n\nUse a pipeline when a resource is produced by a **multi-step transformation**\nof an input (a document, a file, a message) rather than by a simple CRUD write.\n\n## Structure of a family\n\nA **family** is one input shape (e.g. a file format or a message kind). Each\nfamily has the same skeleton:\n\n```\npipelines/<family>/\n abc.py # port contracts (stage interfaces + root interface)\n impl.py # concrete stages + root implementation\n pipeline.yaml # graph spec: nodes, edges, roots, schema\n resources/\n pipeline.yaml # runtime spec consumed by the loader\n patterns/*.yaml # rule fragments (pattern registry)\n banks/<variant>.py # optional variants that re-pin a few slots\n```\n\n## Shared scaffolding\n\n```\npipelines/shared/\n ports.py # RegistryPort, FinalizePort, etc.\n loader.py # build(root_cls) -> wires the graph + engine\n registry.py # resolve_root(...) -> root class for a (kind, variant)\n```\n\n## The loader\n\n```python\ndef build(root_cls, *, instances=None, config=None):\n # merge resources/patterns/*.yaml into a pattern registry\n # bind registry/config into each graph node by id\n # build the schema (state) class and the engine\n root = root_cls()\n root._engine = Engine(graph, state_cls, payload_cls)\n return root\n```\n\n## The registry\n\nA single lookup replaces hand-rolled `if/else` selection:\n\n```python\n_FAMILIES = {\n \"csv\": {\"acme\": {\"heuristic\": AcmeCsvPipeline}},\n \"json\": {\"globex\": {\"heuristic\": GlobexJsonPipeline}},\n}\n\ndef resolve_root(kind, variant, mode=\"heuristic\"):\n return _FAMILIES[kind][variant][mode]\n```\n\nAdding a new variant means adding one class and one registry entry.\n\n## Driving a pipeline\n\nStages are driven explicitly so intermediate state can be persisted:\n\n```python\npipeline = build(resolve_root(kind, variant))\ncontent = pipeline.ingest.materialize(raw_bytes)\nrecords = pipeline.reader.read(content)\nitems = pipeline.normalize.build(records)\n```\n\n## Testing\n\nBecause stages are pure, test each stage in isolation and test the assembled\nroot against **fixture inputs** whose expected outputs are stored as golden\nfiles. A parity test replays fixtures headlessly.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/spec_first_routing.json
Normal file
8
mcp/standards/modules/spec_first_routing.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "spec_first_routing",
|
||||
"content": {
|
||||
"path": "spec_first_routing",
|
||||
"docstring": "# Spec-First Routing\n\n## Summary\n\nThe OpenAPI document is the single source of truth for the HTTP contract.\nHandlers are bound to it by name; there is no decorator router.\n\n---\n\n## The contract\n\n- `openapi.yaml` (or `.json`) declares paths, schemas, parameters, responses,\n and security.\n- A custom `operationId` names the handler function.\n- The framework (`openapi_first`) validates requests/responses against the\n spec and dispatches to the handler whose name equals `operationId`.\n\n## App bootstrap\n\n```python\n# main.py\nfrom openapi_first.app import OpenAPIFirstApp\n\napp = OpenAPIFirstApp(\n openapi_path=\"openapi.yaml\",\n routes_module=interfaces.openapi, # module whose namespace holds handlers\n title=\"<App> API\",\n version=\"0.1.0\",\n lifespan=lifespan,\n)\n```\n\n`lifespan` calls the composition root:\n\n```python\n@asynccontextmanager\nasync def lifespan(app):\n app.state.resources = await init()\n yield\n await shutdown()\n```\n\n## Handler registry\n\n`interfaces/openapi.py` is a flat re-export of every handler, so the framework\ncan resolve `operationId` across the whole app:\n\n```python\nfrom interfaces.item.routes import *\nfrom interfaces.wallet.routes import *\nfrom interfaces.progress.sse import *\n```\n\n## Handler shape\n\nHandlers are thin and synchronous with respect to business logic:\n\n```python\nasync def create_item(request: Request, body: dict):\n svc = request.app.state.resources[\"item_svc\"]\n return await svc.create(Item.model_validate(body))\n```\n\n## Conventions\n\n- `operationId` naming: `list_<resource>`, `create_<resource>`,\n `get_<resource>`, `update_<resource>`, `delete_<resource>`.\n- Read-only endpoints may use a cache decorator keyed on path + query + user.\n- Validation errors -> `400`; not found -> `404`; delete -> `204`.\n- Security is declared once in the spec and enforced by the framework; handlers\n contain no auth code.\n\n## Codegen\n\nThe `openapi_first` library can scaffold models and route stubs from the spec,\nand ships app templates (CRUD app, model app, service). Prefer codegen over\nhand-writing boilerplate.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/testing.json
Normal file
8
mcp/standards/modules/testing.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "testing",
|
||||
"content": {
|
||||
"path": "testing",
|
||||
"docstring": "# Testing\n\n## Summary\n\nTwo test suites: **integration** (the HTTP edge with fakes) and **unit** (pure\ndomain and pipeline logic). Both run under pytest with asyncio auto mode.\n\n---\n\n## Layout\n\n```\ntests/\n integration/ # TestClient(app) + mocked resources\n unit/ # models, converters, pipeline stages/roots\n conftest.py # fixtures\n```\n\n## Integration tests\n\n- Build the app with `TestClient`.\n- Replace `app.state.resources` with fake repositories/services so no database\n is required.\n- Patch the auth introspection call (`httpx.AsyncClient.post`) to return a\n canned identity.\n- Assert status codes and bodies for list/get/create/update/delete, plus\n `400`, `404`, `204`, and the streaming endpoint's fallback.\n\n```python\nclient = TestClient(app)\napp.state.resources = {\"item_svc\": FakeItemService(), ...}\n\ndef test_create_item():\n r = client.post(\"/items\", json={\"name\": \"x\"})\n assert r.status_code == 200\n```\n\n## Unit tests\n\n- Domain models and converters: pure, no I/O.\n- Pipeline stages: feed a fixture state, assert the transformed state.\n- Assembled pipeline roots: **fixture parity** — replay a stored fixture input\n and compare against a golden output file.\n\n## Config-consistency tests\n\nFor rule-driven pipelines, add a test that cross-references rules against the\ndeclared pattern registry: every flag emitted must be defined; every defined\nflag must be reachable. This catches drift without running the pipeline.\n\n## Conventions\n\n- `asyncio_mode = \"auto\"`; no manual event loops.\n- Fixtures are checked into the repo (inputs + golden outputs).\n- Tests skip (not fail) when a required fixture is absent.\n- Do not mock the domain; mock only the infrastructure edge.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
mcp/standards/modules/worker_and_progress.json
Normal file
8
mcp/standards/modules/worker_and_progress.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "worker_and_progress",
|
||||
"content": {
|
||||
"path": "worker_and_progress",
|
||||
"docstring": "# Worker & Progress\n\n## Summary\n\nLong-running work runs in a background worker. Jobs are durable documents that\nmove through an explicit status machine. Progress is streamed to clients.\n\n---\n\n## The status machine\n\n```\npending -> processing -> completed\n \\-> failed (retried up to N times)\n \\-> paused (waiting for human input)\n```\n\n- `pending` — queued, eligible for pickup.\n- `processing` — claimed by a worker.\n- `paused` — blocked on a human-in-the-loop question.\n- `failed` — error; `retry_count` increments until a max, then skipped.\n- `completed` — terminal success.\n\n## The poll loop\n\n```python\nasync def poll(resources):\n backoff = MIN_POLL\n while True:\n jobs = await job_svc.search(filter={\"status\": \"pending\"}, limit=100)\n backoff = MIN_POLL if jobs else min(backoff * 2, MAX_POLL)\n for job in jobs:\n if (job.retry_count or 0) >= MAX_RETRIES:\n continue\n await job_svc.update_status(job.id, \"processing\")\n try:\n await process(job, resources)\n except PauseForResolution:\n await job_svc.update_status(job.id, \"paused\")\n except Exception as e:\n await job_svc.update_status(\n job.id, \"failed\", error=str(e),\n retry_count=(job.retry_count or 0) + 1,\n )\n await asyncio.sleep(backoff)\n```\n\nTunables live in environment variables: `WORKER_POLL_MIN`, `WORKER_POLL_MAX`,\n`WORKER_MAX_RETRIES`.\n\n## Progress events\n\nA worker publishes structured progress events to a **bus** (a port). The HTTP\nedge exposes a streaming endpoint that replays events for a given job id.\n\n```python\nclass ProgressEvent(BaseModel):\n job_id: str\n step: str\n status: str # started | progress | completed | skipped | paused\n message: dict | None = None\n```\n\nThe bus is an in-memory adapter by default; because it is a port, it can be\nswapped for a durable broker. Emit from the orchestrator at each stage:\n\n```python\nawait self.bus.publish(ProgressEvent(job_id=jid, step=\"normalize\",\n status=\"completed\", message={\"count\": n}))\n```\n\n## Streaming endpoint\n\n```python\nasync def stream_job_events(request, id):\n bus = request.app.state.resources.get(\"progress_bus\")\n if bus is None:\n return {\"events\": []}\n\n async def gen():\n sent = 0\n while True:\n events = bus.get_events(id)\n for e in events[sent:]:\n yield f\"data: {e.model_dump_json(exclude_none=True)}\\n\\n\"\n sent += 1\n await asyncio.sleep(0.5)\n\n return StreamingResponse(gen(), media_type=\"text/event-stream\")\n```\n\n> **Known limitation.** The default in-memory bus is per-process. If the worker\n> and the API run as separate processes, events published by the worker are not\n> visible to the API. For production, back the bus with a shared broker. The\n> port boundary makes this a one-adapter change.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
72
mcp/standards/nav.json
Normal file
72
mcp/standards/nav.json
Normal file
@@ -0,0 +1,72 @@
|
||||
[
|
||||
{
|
||||
"module": "index",
|
||||
"resource": "docs://modules/index",
|
||||
"title": "Home"
|
||||
},
|
||||
{
|
||||
"module": "overview",
|
||||
"resource": "docs://modules/overview",
|
||||
"title": "Overview"
|
||||
},
|
||||
{
|
||||
"module": "layering_rules",
|
||||
"resource": "docs://modules/layering_rules",
|
||||
"title": "Layering Rules"
|
||||
},
|
||||
{
|
||||
"module": "composition_root",
|
||||
"resource": "docs://modules/composition_root",
|
||||
"title": "Composition Root"
|
||||
},
|
||||
{
|
||||
"module": "spec_first_routing",
|
||||
"resource": "docs://modules/spec_first_routing",
|
||||
"title": "Spec-First Routing"
|
||||
},
|
||||
{
|
||||
"module": "crud_template",
|
||||
"resource": "docs://modules/crud_template",
|
||||
"title": "CRUD Template"
|
||||
},
|
||||
{
|
||||
"module": "pipeline_scaffold",
|
||||
"resource": "docs://modules/pipeline_scaffold",
|
||||
"title": "Pipeline Scaffold"
|
||||
},
|
||||
{
|
||||
"module": "worker_and_progress",
|
||||
"resource": "docs://modules/worker_and_progress",
|
||||
"title": "Worker & Progress"
|
||||
},
|
||||
{
|
||||
"module": "human_in_loop",
|
||||
"resource": "docs://modules/human_in_loop",
|
||||
"title": "Human-in-the-Loop"
|
||||
},
|
||||
{
|
||||
"module": "infra_adaptors",
|
||||
"resource": "docs://modules/infra_adaptors",
|
||||
"title": "Infra Adapters"
|
||||
},
|
||||
{
|
||||
"module": "testing",
|
||||
"resource": "docs://modules/testing",
|
||||
"title": "Testing"
|
||||
},
|
||||
{
|
||||
"module": "build_recipe",
|
||||
"resource": "docs://modules/build_recipe",
|
||||
"title": "Build Recipe"
|
||||
},
|
||||
{
|
||||
"module": "genericity_matrix",
|
||||
"resource": "docs://modules/genericity_matrix",
|
||||
"title": "Genericity Matrix"
|
||||
},
|
||||
{
|
||||
"module": "library_registry",
|
||||
"resource": "docs://modules/library_registry",
|
||||
"title": "Library Registry"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user