@senpi/runtime 3.0.9-dev.20260710204324 → 3.0.9-dev.feat-strategy-compos.20260716233548
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/composer-commands.d.ts +37 -0
- package/dist/cli/composer-commands.d.ts.map +1 -0
- package/dist/cli/composer-commands.js +396 -0
- package/dist/cli/composer-commands.js.map +1 -0
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +2 -0
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/cli/validate-runtime-config.mjs +190 -0
- package/dist/composer/AUTHORING.md +435 -0
- package/dist/composer/__init__.py +29 -0
- package/dist/composer/authoring/__init__.py +59 -0
- package/dist/composer/authoring/answers.py +297 -0
- package/dist/composer/authoring/archetypes/__init__.py +26 -0
- package/dist/composer/authoring/archetypes/_common.py +410 -0
- package/dist/composer/authoring/archetypes/breakout.py +134 -0
- package/dist/composer/authoring/archetypes/classifier.py +109 -0
- package/dist/composer/authoring/archetypes/skeleton.py +76 -0
- package/dist/composer/authoring/archetypes/thesis_fund.py +103 -0
- package/dist/composer/authoring/archetypes/trend_momentum.py +132 -0
- package/dist/composer/authoring/builder.py +191 -0
- package/dist/composer/authoring/emit.py +264 -0
- package/dist/composer/authoring/gaps.py +115 -0
- package/dist/composer/authoring/generate.py +131 -0
- package/dist/composer/authoring/presets.py +168 -0
- package/dist/composer/authoring/runtime_emit.py +266 -0
- package/dist/composer/authoring/strategy_manifest.py +94 -0
- package/dist/composer/cli.py +487 -0
- package/dist/composer/compiler/__init__.py +22 -0
- package/dist/composer/compiler/compile.py +739 -0
- package/dist/composer/examples/breakout_swing.graph.yaml +121 -0
- package/dist/composer/examples/conviction_scorer.graph.yaml +216 -0
- package/dist/composer/examples/macro_thesis_basket.graph.yaml +247 -0
- package/dist/composer/examples/pre_listing_classifier.graph.yaml +204 -0
- package/dist/composer/examples/trend_momentum_majors.graph.yaml +133 -0
- package/dist/composer/generated/README.md +22 -0
- package/dist/composer/generated/catalog.md +118 -0
- package/dist/composer/generated/describe.md +1168 -0
- package/dist/composer/generated/graph.schema.json +1946 -0
- package/dist/composer/primitives/__init__.py +320 -0
- package/dist/composer/primitives/_candles.py +76 -0
- package/dist/composer/primitives/adapter.py +363 -0
- package/dist/composer/primitives/emitters.py +142 -0
- package/dist/composer/primitives/filters.py +71 -0
- package/dist/composer/primitives/indicators.py +326 -0
- package/dist/composer/primitives/rankers.py +40 -0
- package/dist/composer/primitives/scorers.py +100 -0
- package/dist/composer/primitives/sources.py +979 -0
- package/dist/composer/primitives/stateful.py +254 -0
- package/dist/composer/pure_fn/__init__.py +34 -0
- package/dist/composer/pure_fn/checks.py +39 -0
- package/dist/composer/pure_fn/models.py +177 -0
- package/dist/composer/pure_fn/purity.py +311 -0
- package/dist/composer/pure_fn/runtests.py +155 -0
- package/dist/composer/registry/__init__.py +40 -0
- package/dist/composer/registry/generate.py +425 -0
- package/dist/composer/registry/mcp-tools-snapshot.json +193 -0
- package/dist/composer/registry/mcp_closure.py +311 -0
- package/dist/composer/registry/models.py +417 -0
- package/dist/composer/registry/nodes.py +1297 -0
- package/dist/composer/requirements.txt +21 -0
- package/dist/composer/scaffold_path.py +118 -0
- package/dist/composer/templates/__init__.py +13 -0
- package/dist/composer/templates/scan.py.j2 +34 -0
- package/dist/composer/validator/__init__.py +29 -0
- package/dist/composer/validator/desugar.py +133 -0
- package/dist/composer/validator/errors.py +227 -0
- package/dist/composer/validator/graph_checks.py +443 -0
- package/dist/composer/validator/loader.py +110 -0
- package/dist/composer/validator/schema_checks.py +147 -0
- package/dist/composer/validator/validate.py +86 -0
- package/dist/composer/vendoring.py +103 -0
- package/dist/composer/verbs/__init__.py +49 -0
- package/dist/composer/verbs/_box.py +308 -0
- package/dist/composer/verbs/check.py +254 -0
- package/dist/composer/verbs/close.py +494 -0
- package/dist/composer/verbs/deploy.py +279 -0
- package/dist/composer/verbs/docs.py +30 -0
- package/dist/composer/verbs/fund.py +498 -0
- package/dist/composer/verbs/install.py +586 -0
- package/dist/composer/verbs/runtime_validate.py +422 -0
- package/dist/composer/verbs/smoke.py +134 -0
- package/dist/composer/verbs/status.py +383 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -9
- package/dist/index.js.map +1 -1
- package/dist/runtime/load-runtime-config.d.ts +17 -0
- package/dist/runtime/load-runtime-config.d.ts.map +1 -1
- package/dist/runtime/load-runtime-config.js +2 -2
- package/dist/runtime/load-runtime-config.js.map +1 -1
- package/dist/runtime-api/config.d.ts +2 -2
- package/dist/runtime-api/config.js +4 -4
- package/dist/runtime-api/config.js.map +1 -1
- package/dist/runtime-api/handlers/ingest-signals.d.ts +3 -5
- package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -1
- package/dist/runtime-api/handlers/ingest-signals.js +5 -14
- package/dist/runtime-api/handlers/ingest-signals.js.map +1 -1
- package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
- package/dist/runtime-api/intake/intake-mount.js +2 -8
- package/dist/runtime-api/intake/intake-mount.js.map +1 -1
- package/dist/skills-manager/manifest.d.ts.map +1 -1
- package/dist/skills-manager/manifest.js +1 -3
- package/dist/skills-manager/manifest.js.map +1 -1
- package/dist/skills-manager/skills-manifest.json +1 -3
- package/package.json +4 -2
- package/dist/runtime-api/intake/ingest-mutex.d.ts +0 -16
- package/dist/runtime-api/intake/ingest-mutex.d.ts.map +0 -1
- package/dist/runtime-api/intake/ingest-mutex.js +0 -18
- package/dist/runtime-api/intake/ingest-mutex.js.map +0 -1
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"""generate — deterministic artifact generation from the registry (D4, D6).
|
|
2
|
+
|
|
3
|
+
Everything the agent and the tooling read is generated here from ``NODES`` alone, so it
|
|
4
|
+
cannot drift from the models the validator/compiler use. Three artifacts, written to
|
|
5
|
+
``composer/generated/`` (see that directory's README):
|
|
6
|
+
|
|
7
|
+
graph.schema.json — JSON Schema for the graph document format (D6). YAML is authored;
|
|
8
|
+
this JSON Schema is canonical. Per-node param constraints are
|
|
9
|
+
derived via if/then on the node name. YAML anchors/merge keys are
|
|
10
|
+
banned at the document level (noted here; enforced in slice 2).
|
|
11
|
+
catalog.md — the ~1-2k-token index: node names + one-liners + param summaries.
|
|
12
|
+
describe.md — full ports/params/example/cost per node (`composer describe`).
|
|
13
|
+
|
|
14
|
+
Determinism is a hard requirement (the golden-file oracle): stable ordering
|
|
15
|
+
(``sorted_nodes``), sorted JSON keys, no timestamps, trailing newline. Run twice → byte
|
|
16
|
+
identical. Entry point:
|
|
17
|
+
|
|
18
|
+
python -m composer.registry.generate # write artifacts
|
|
19
|
+
python -m composer.registry.generate --check # exit 1 if regeneration differs
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
from .models import NodeDef, Param, PortType, SCHEMA_VERSION
|
|
29
|
+
from .nodes import KIND_ORDER, sorted_nodes
|
|
30
|
+
|
|
31
|
+
GENERATED_DIR = Path(__file__).resolve().parent.parent / "generated"
|
|
32
|
+
SCHEMA_PATH = GENERATED_DIR / "graph.schema.json"
|
|
33
|
+
CATALOG_PATH = GENERATED_DIR / "catalog.md"
|
|
34
|
+
DESCRIBE_PATH = GENERATED_DIR / "describe.md"
|
|
35
|
+
|
|
36
|
+
_JSON_TYPE = {
|
|
37
|
+
"int": "integer",
|
|
38
|
+
"float": "number",
|
|
39
|
+
"str": "string",
|
|
40
|
+
"bool": "boolean",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# --------------------------------------------------------------------------- JSON Schema
|
|
45
|
+
|
|
46
|
+
def _param_schema(param: Param) -> dict:
|
|
47
|
+
"""JSON Schema fragment for a single param (with bounds, choices, default)."""
|
|
48
|
+
if param.type == "list[str]":
|
|
49
|
+
node: dict = {"type": "array", "items": {"type": "string"}}
|
|
50
|
+
elif param.type == "map_str_float":
|
|
51
|
+
node = {"type": "object", "additionalProperties": {"type": "number"}}
|
|
52
|
+
elif param.type == "map_str_json":
|
|
53
|
+
# str-keyed JSON object passed verbatim to an MCP tool (mcp_read escape hatch); values
|
|
54
|
+
# are arbitrary JSON, so no additionalProperties constraint beyond "it is an object".
|
|
55
|
+
node = {"type": "object"}
|
|
56
|
+
else:
|
|
57
|
+
node = {"type": _JSON_TYPE[param.type]}
|
|
58
|
+
if param.minimum is not None:
|
|
59
|
+
node["minimum"] = param.minimum
|
|
60
|
+
if param.maximum is not None:
|
|
61
|
+
node["maximum"] = param.maximum
|
|
62
|
+
if param.choices is not None:
|
|
63
|
+
node["enum"] = list(param.choices)
|
|
64
|
+
if param.default is not None:
|
|
65
|
+
node["default"] = list(param.default) if isinstance(param.default, tuple) else param.default
|
|
66
|
+
node["description"] = param.doc
|
|
67
|
+
return node
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _params_object_schema(node: NodeDef) -> dict:
|
|
71
|
+
props = {p.name: _param_schema(p) for p in node.params}
|
|
72
|
+
required = [p.name for p in node.params if p.default is None]
|
|
73
|
+
obj: dict = {"type": "object", "additionalProperties": False, "properties": props}
|
|
74
|
+
if required:
|
|
75
|
+
obj["required"] = sorted(required)
|
|
76
|
+
return obj
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _pure_fn_port_schema() -> dict:
|
|
80
|
+
"""A pure_fn declared port: name + a type from the closed PortType vocabulary."""
|
|
81
|
+
return {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": False,
|
|
84
|
+
"required": ["name", "type"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"name": {"type": "string"},
|
|
87
|
+
"type": {"enum": [t.value for t in PortType]},
|
|
88
|
+
"doc": {"type": "string"},
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _pure_fn_param_schema() -> dict:
|
|
94
|
+
"""A pure_fn literal param (config, not an edge): name + type + optional default."""
|
|
95
|
+
return {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"additionalProperties": False,
|
|
98
|
+
"required": ["name", "type"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"name": {"type": "string"},
|
|
101
|
+
"type": {"enum": ["int", "float", "str", "bool", "list[str]", "map_str_float"]},
|
|
102
|
+
"default": {},
|
|
103
|
+
"doc": {"type": "string"},
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _pure_fn_test_schema() -> dict:
|
|
109
|
+
"""An inline fixture case: named input values -> expected outputs (run at validate time)."""
|
|
110
|
+
return {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"additionalProperties": False,
|
|
113
|
+
"required": ["name", "inputs", "expect"],
|
|
114
|
+
"properties": {
|
|
115
|
+
"name": {"type": "string"},
|
|
116
|
+
"inputs": {"type": "object"},
|
|
117
|
+
"expect": {"type": "object"},
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def build_schema() -> dict:
|
|
123
|
+
nodes = sorted_nodes()
|
|
124
|
+
names = sorted(n.name for n in nodes)
|
|
125
|
+
|
|
126
|
+
defs = {f"params_{n.name}": _params_object_schema(n) for n in nodes}
|
|
127
|
+
all_of = [
|
|
128
|
+
{
|
|
129
|
+
"if": {"properties": {"node": {"const": n.name}}},
|
|
130
|
+
"then": {"properties": {"with": {"$ref": f"#/$defs/params_{n.name}"}}},
|
|
131
|
+
}
|
|
132
|
+
for n in sorted(nodes, key=lambda n: n.name)
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
137
|
+
"$id": "https://senpi.ai/strategy-composer/graph.schema.json",
|
|
138
|
+
"title": "Strategy Composer Graph",
|
|
139
|
+
"description": (
|
|
140
|
+
"Typed dataflow DAG for a trading strategy (D1). YAML is authored; this JSON "
|
|
141
|
+
"Schema is canonical (D6). YAML anchors and merge keys are banned at the "
|
|
142
|
+
"document level (enforced in the validator, slice 2). Generated from the node "
|
|
143
|
+
"registry — do not edit by hand."
|
|
144
|
+
),
|
|
145
|
+
"type": "object",
|
|
146
|
+
"additionalProperties": False,
|
|
147
|
+
"required": ["nodes"],
|
|
148
|
+
"properties": {
|
|
149
|
+
"version": {"const": SCHEMA_VERSION, "description": "Registry/schema version."},
|
|
150
|
+
"metadata": {
|
|
151
|
+
"type": "object",
|
|
152
|
+
"description": "Interview answers and authoring notes (D7); free-form.",
|
|
153
|
+
},
|
|
154
|
+
"nodes": {
|
|
155
|
+
"type": "array",
|
|
156
|
+
"minItems": 1,
|
|
157
|
+
"description": (
|
|
158
|
+
"The DAG nodes. Each is EITHER a primitive node binding a registry "
|
|
159
|
+
"`node:` to params, OR a per-graph `kind: pure_fn` node (D2 kind 2) "
|
|
160
|
+
"carrying declared typed inputs/outputs and an inline `fn:` body. The "
|
|
161
|
+
"per-node `node`-keyed branches below apply only to primitive nodes; a "
|
|
162
|
+
"pure_fn node (no `node:`) satisfies them vacuously and is checked by the "
|
|
163
|
+
"validator's purity + inline-test stage."
|
|
164
|
+
),
|
|
165
|
+
"items": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"additionalProperties": False,
|
|
168
|
+
"required": ["id"],
|
|
169
|
+
"properties": {
|
|
170
|
+
"id": {"type": "string", "description": "Unique node id within the graph."},
|
|
171
|
+
"node": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"enum": names,
|
|
174
|
+
"description": "Registry node name (primitive nodes only).",
|
|
175
|
+
},
|
|
176
|
+
"with": {"type": "object", "description": "Node parameters."},
|
|
177
|
+
"kind": {
|
|
178
|
+
"enum": ["pure_fn"],
|
|
179
|
+
"description": "Marks a per-graph pure-function node (D2 kind 2).",
|
|
180
|
+
},
|
|
181
|
+
"inputs": {
|
|
182
|
+
"type": "array",
|
|
183
|
+
"items": _pure_fn_port_schema(),
|
|
184
|
+
"description": "pure_fn declared typed input ports.",
|
|
185
|
+
},
|
|
186
|
+
"outputs": {
|
|
187
|
+
"type": "array",
|
|
188
|
+
"items": _pure_fn_port_schema(),
|
|
189
|
+
"description": "pure_fn declared typed output ports.",
|
|
190
|
+
},
|
|
191
|
+
"params": {
|
|
192
|
+
"type": "array",
|
|
193
|
+
"items": _pure_fn_param_schema(),
|
|
194
|
+
"description": "pure_fn literal params (config, not edges).",
|
|
195
|
+
},
|
|
196
|
+
"fn": {
|
|
197
|
+
"type": "string",
|
|
198
|
+
"description": "pure_fn body: exactly one top-level `def` (purity-checked).",
|
|
199
|
+
},
|
|
200
|
+
"tests": {
|
|
201
|
+
"type": "array",
|
|
202
|
+
"items": _pure_fn_test_schema(),
|
|
203
|
+
"description": "pure_fn inline fixture tests, run at validate time.",
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
"allOf": all_of,
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
"edges": {
|
|
210
|
+
"type": "array",
|
|
211
|
+
"description": "Wiring: from 'nodeId.outPort' to 'nodeId.inPort' (D1).",
|
|
212
|
+
"items": {
|
|
213
|
+
"type": "object",
|
|
214
|
+
"additionalProperties": False,
|
|
215
|
+
"required": ["from", "to"],
|
|
216
|
+
"properties": {
|
|
217
|
+
"from": {"type": "string", "pattern": "^[^.]+\\.[^.]+$"},
|
|
218
|
+
"to": {"type": "string", "pattern": "^[^.]+\\.[^.]+$"},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
"pipeline": {
|
|
223
|
+
"type": "array",
|
|
224
|
+
"items": {"type": "string"},
|
|
225
|
+
"description": (
|
|
226
|
+
"RESERVED shorthand marker for the common linear case (D1/D6). "
|
|
227
|
+
"Desugaring into nodes+edges is slice 2 and is NOT built here."
|
|
228
|
+
),
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
"$defs": defs,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def render_schema() -> str:
|
|
236
|
+
return json.dumps(build_schema(), indent=2, sort_keys=True) + "\n"
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# ------------------------------------------------------------------------------- catalog
|
|
240
|
+
|
|
241
|
+
def _param_summary(param: Param) -> str:
|
|
242
|
+
if param.default is None:
|
|
243
|
+
val = "required"
|
|
244
|
+
elif param.type == "list[str]":
|
|
245
|
+
val = "[" + ", ".join(param.default) + "]"
|
|
246
|
+
elif param.type in ("map_str_float", "map_str_json"):
|
|
247
|
+
val = "{...}" if param.default else "{}"
|
|
248
|
+
else:
|
|
249
|
+
val = repr(param.default) if param.type == "str" else str(param.default)
|
|
250
|
+
body = f"{param.name} ({param.type}={val}"
|
|
251
|
+
if param.minimum is not None or param.maximum is not None:
|
|
252
|
+
body += f", {_bound(param, param.minimum)}..{_bound(param, param.maximum)}"
|
|
253
|
+
return body + ")"
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _bound(param: Param, value) -> str:
|
|
257
|
+
if value is None:
|
|
258
|
+
return ""
|
|
259
|
+
return str(int(value)) if param.type == "int" else str(value)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def render_catalog() -> str:
|
|
263
|
+
lines = [
|
|
264
|
+
"# Strategy Composer — Node Catalog",
|
|
265
|
+
"",
|
|
266
|
+
f"Schema version: {SCHEMA_VERSION}. Generated from composer/registry/nodes.py — "
|
|
267
|
+
"do not edit by hand.",
|
|
268
|
+
"",
|
|
269
|
+
"Progressive-disclosure index (D4): node names, one-liners, param summaries. "
|
|
270
|
+
"`composer describe <node>` gives full ports/params/examples.",
|
|
271
|
+
"",
|
|
272
|
+
"## scoring",
|
|
273
|
+
"",
|
|
274
|
+
"Bespoke scoring/classification is a `kind: pure_fn` node (typed ports, one `def`, "
|
|
275
|
+
"math/statistics only, inline tests), never a primitive; `hard_gates_all` covers "
|
|
276
|
+
"simple gate-and-weigh. Repeated shapes promote post-MVP.",
|
|
277
|
+
]
|
|
278
|
+
nodes = sorted_nodes()
|
|
279
|
+
for kind in KIND_ORDER:
|
|
280
|
+
kind_nodes = [n for n in nodes if n.kind.value == kind]
|
|
281
|
+
if not kind_nodes:
|
|
282
|
+
continue
|
|
283
|
+
lines.append("")
|
|
284
|
+
lines.append(f"## {kind}")
|
|
285
|
+
lines.append("")
|
|
286
|
+
for node in kind_nodes:
|
|
287
|
+
lines.append(f"- **{node.name}** — {node.summary}")
|
|
288
|
+
if node.params:
|
|
289
|
+
params = "; ".join(_param_summary(p) for p in node.params)
|
|
290
|
+
lines.append(f" - params: {params}")
|
|
291
|
+
return "\n".join(lines) + "\n"
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# ------------------------------------------------------------------------------ describe
|
|
295
|
+
|
|
296
|
+
def _port_line(port) -> str:
|
|
297
|
+
line = f"- `{port.name}`: {port.type.value} — {port.doc}"
|
|
298
|
+
annotations = []
|
|
299
|
+
if not port.required:
|
|
300
|
+
annotations.append("optional")
|
|
301
|
+
if port.merge:
|
|
302
|
+
annotations.append("merge")
|
|
303
|
+
if annotations:
|
|
304
|
+
line += f" [{', '.join(annotations)}]"
|
|
305
|
+
return line
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _param_detail(param: Param) -> str:
|
|
309
|
+
if param.default is None:
|
|
310
|
+
default = "required"
|
|
311
|
+
elif param.type == "list[str]":
|
|
312
|
+
default = "[" + ", ".join(param.default) + "]"
|
|
313
|
+
elif param.type in ("map_str_float", "map_str_json"):
|
|
314
|
+
default = json.dumps(param.default, sort_keys=True)
|
|
315
|
+
else:
|
|
316
|
+
default = repr(param.default) if param.type == "str" else str(param.default)
|
|
317
|
+
line = f"- `{param.name}`: {param.type} (default: {default})"
|
|
318
|
+
if param.minimum is not None or param.maximum is not None:
|
|
319
|
+
line += f" [{_bound(param, param.minimum)}..{_bound(param, param.maximum)}]"
|
|
320
|
+
if param.choices is not None:
|
|
321
|
+
line += f" choices: {list(param.choices)}"
|
|
322
|
+
return line + f" — {param.doc}"
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _describe_node(node) -> list[str]:
|
|
326
|
+
"""The reference block for ONE node (shared by the full describe.md and the per-node
|
|
327
|
+
`composer describe <node>` verb, so the two never drift)."""
|
|
328
|
+
lines = [
|
|
329
|
+
f"## {node.name} ({node.kind.value})",
|
|
330
|
+
"",
|
|
331
|
+
node.doc,
|
|
332
|
+
"",
|
|
333
|
+
"Inputs:",
|
|
334
|
+
]
|
|
335
|
+
lines.extend([_port_line(p) for p in node.inputs] or ["- (none)"])
|
|
336
|
+
lines.append("")
|
|
337
|
+
lines.append("Outputs:")
|
|
338
|
+
lines.extend([_port_line(p) for p in node.outputs] or ["- (none)"])
|
|
339
|
+
lines.append("")
|
|
340
|
+
lines.append("Params:")
|
|
341
|
+
lines.extend([_param_detail(p) for p in node.params] or ["- (none)"])
|
|
342
|
+
lines.append("")
|
|
343
|
+
lines.append(f"Cost: {node.cost}")
|
|
344
|
+
lines.append("")
|
|
345
|
+
lines.append("Example:")
|
|
346
|
+
lines.append("```yaml")
|
|
347
|
+
lines.append(node.example.rstrip("\n"))
|
|
348
|
+
lines.append("```")
|
|
349
|
+
return lines
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def render_describe() -> str:
|
|
353
|
+
lines = [
|
|
354
|
+
"# Strategy Composer — Node Reference",
|
|
355
|
+
"",
|
|
356
|
+
f"Schema version: {SCHEMA_VERSION}. Generated from composer/registry/nodes.py — "
|
|
357
|
+
"do not edit by hand.",
|
|
358
|
+
]
|
|
359
|
+
for node in sorted_nodes():
|
|
360
|
+
lines.append("")
|
|
361
|
+
lines.extend(_describe_node(node))
|
|
362
|
+
return "\n".join(lines) + "\n"
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def render_describe_node(name: str) -> str:
|
|
366
|
+
"""Render the reference block for a single node by name (the `composer describe` verb).
|
|
367
|
+
|
|
368
|
+
Raises KeyError with the nearest-alternatives hint when the name is unknown, mirroring
|
|
369
|
+
the validator's unknown-node teaching error so the surface stays consistent.
|
|
370
|
+
"""
|
|
371
|
+
from . import NODES_BY_NAME
|
|
372
|
+
|
|
373
|
+
node = NODES_BY_NAME.get(name)
|
|
374
|
+
if node is None:
|
|
375
|
+
known = sorted(NODES_BY_NAME)
|
|
376
|
+
near = [n for n in known if name in n or n in name or n[:3] == name[:3]]
|
|
377
|
+
hint = f" Did you mean: {', '.join(near[:4])}?" if near else ""
|
|
378
|
+
raise KeyError(
|
|
379
|
+
f"unknown node {name!r}.{hint} Run `composer catalog` for the full index."
|
|
380
|
+
)
|
|
381
|
+
return "\n".join(_describe_node(node)) + "\n"
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# -------------------------------------------------------------------------------- driver
|
|
385
|
+
|
|
386
|
+
ARTIFACTS = {
|
|
387
|
+
"graph.schema.json": (SCHEMA_PATH, render_schema),
|
|
388
|
+
"catalog.md": (CATALOG_PATH, render_catalog),
|
|
389
|
+
"describe.md": (DESCRIBE_PATH, render_describe),
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def write_all() -> None:
|
|
394
|
+
GENERATED_DIR.mkdir(parents=True, exist_ok=True)
|
|
395
|
+
for _name, (path, render) in ARTIFACTS.items():
|
|
396
|
+
path.write_text(render(), encoding="utf-8")
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def check_all() -> list[str]:
|
|
400
|
+
"""Return the list of artifact names whose committed content differs from a fresh render."""
|
|
401
|
+
stale = []
|
|
402
|
+
for name, (path, render) in ARTIFACTS.items():
|
|
403
|
+
current = path.read_text(encoding="utf-8") if path.exists() else None
|
|
404
|
+
if current != render():
|
|
405
|
+
stale.append(name)
|
|
406
|
+
return stale
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def main(argv: list[str] | None = None) -> int:
|
|
410
|
+
argv = sys.argv[1:] if argv is None else argv
|
|
411
|
+
if "--check" in argv:
|
|
412
|
+
stale = check_all()
|
|
413
|
+
if stale:
|
|
414
|
+
print("stale generated artifacts (run `python -m composer.registry.generate`): "
|
|
415
|
+
+ ", ".join(stale), file=sys.stderr)
|
|
416
|
+
return 1
|
|
417
|
+
print("generated artifacts are up to date")
|
|
418
|
+
return 0
|
|
419
|
+
write_all()
|
|
420
|
+
print(f"wrote {len(ARTIFACTS)} artifacts to {GENERATED_DIR}")
|
|
421
|
+
return 0
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
if __name__ == "__main__":
|
|
425
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Trimmed snapshot of the live prod MCP tools/list (names + input-schema hash only) for the registry closure check. Regenerate via MCP_SNAPSHOT_REFRESH=1 pytest python-tests/composer/test_mcp_closure.py (fetches https://mcp.prod.senpi.ai/mcp, unauthenticated initialize + tools/list). Do not hand-edit.",
|
|
3
|
+
"source": "https://mcp.prod.senpi.ai/mcp",
|
|
4
|
+
"tool_count": 62,
|
|
5
|
+
"tools": {
|
|
6
|
+
"account_get_historical_info": {
|
|
7
|
+
"schema_sha256": "343e4681e9c8d500ccd29de71e8cf39a7ee013f438f3e4e219ce25f40c89f6f9"
|
|
8
|
+
},
|
|
9
|
+
"account_get_portfolio": {
|
|
10
|
+
"schema_sha256": "f9192e9e827d7d1c112bb7f25721e6a2e01955ecc150e238a6bd6a7beecadced"
|
|
11
|
+
},
|
|
12
|
+
"arena_leaderboard": {
|
|
13
|
+
"schema_sha256": "ce4c834ff0444dddf743e5d9f3a366d6e30ab5ee96dcc512e94ad106deb6fad2"
|
|
14
|
+
},
|
|
15
|
+
"arena_pool": {
|
|
16
|
+
"schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
|
|
17
|
+
},
|
|
18
|
+
"arena_prizes": {
|
|
19
|
+
"schema_sha256": "421d6192dfab0707b947e318d96d78a94fc80d8d63d99f275093a04e88b05917"
|
|
20
|
+
},
|
|
21
|
+
"arena_roe_chart": {
|
|
22
|
+
"schema_sha256": "ea0f18eda7b70ce3b3d2ddf84f44f7ec8029549c403c55dcf5ee35032c62d535"
|
|
23
|
+
},
|
|
24
|
+
"cancel_order": {
|
|
25
|
+
"schema_sha256": "27093457765e49342e60be640f5160cf839e832942f6f9532fab1246961654c3"
|
|
26
|
+
},
|
|
27
|
+
"close_position": {
|
|
28
|
+
"schema_sha256": "a9992eecf1e84e639be4356e36f79c79319382ac354f2b254f73f8b3b12de829"
|
|
29
|
+
},
|
|
30
|
+
"create_position": {
|
|
31
|
+
"schema_sha256": "01d65e0cb9cf1aec73f37d8246a7f2b75a7826f83f6dfc5c3ee07cf9b0c4b80d"
|
|
32
|
+
},
|
|
33
|
+
"discovery_get_open_position_realized_pnl": {
|
|
34
|
+
"schema_sha256": "c40eeebdd3242b3a97821a9f45c99cdc1bfe476482bb660b9ccdf65055632358"
|
|
35
|
+
},
|
|
36
|
+
"discovery_get_top_strategies": {
|
|
37
|
+
"schema_sha256": "bf9c4d412268be3c8f2aae23c8af4b5a69fb12f15ccdfd487b6fc970ea5f674f"
|
|
38
|
+
},
|
|
39
|
+
"discovery_get_top_traders": {
|
|
40
|
+
"schema_sha256": "38d62d42db6811ff6e2b707036bc92110318d32ee2a405896b0d598057db441e"
|
|
41
|
+
},
|
|
42
|
+
"discovery_get_trader_history": {
|
|
43
|
+
"schema_sha256": "fef06f73272f9a637f937d898be4fddd23b47150062f0bd36eebe3ca0633cc60"
|
|
44
|
+
},
|
|
45
|
+
"discovery_get_trader_state": {
|
|
46
|
+
"schema_sha256": "9a07274df7f2d5721478100cf5e4b9b5b1ef41ccee25b7e94ff01324a94c20f4"
|
|
47
|
+
},
|
|
48
|
+
"edit_position": {
|
|
49
|
+
"schema_sha256": "a5b8e3a7f2ac89b0e262354f74a18aa56239379d37b65897e8d7465865f17b00"
|
|
50
|
+
},
|
|
51
|
+
"estimate_custom_strategy_positions_opening": {
|
|
52
|
+
"schema_sha256": "341b278c6bc5bc7905facf44a39d55d2aa60cda02df0cd231629f030a94b2ba0"
|
|
53
|
+
},
|
|
54
|
+
"execution_estimate_position_opening": {
|
|
55
|
+
"schema_sha256": "ff038e8aeb0baf8dcee1f1c8e6ddb7d2b20ee5ef904a521fa15b548eb4eaad22"
|
|
56
|
+
},
|
|
57
|
+
"execution_get_closed_position_details": {
|
|
58
|
+
"schema_sha256": "3e4eb5ae16ae78866979ee466e52a15716b090dee82669515f584cecf7e8592f"
|
|
59
|
+
},
|
|
60
|
+
"execution_get_open_position_details": {
|
|
61
|
+
"schema_sha256": "406eb43e19077ce59e967b4b73100e72e0bfdec4205ceb9ef82fbc412d9a3d24"
|
|
62
|
+
},
|
|
63
|
+
"execution_get_order_status": {
|
|
64
|
+
"schema_sha256": "22e5705787eb675c913a28f4fa324f80b127a147141a50d1ac5ed1e7ccfb7d2b"
|
|
65
|
+
},
|
|
66
|
+
"get_loyalty_tiers": {
|
|
67
|
+
"schema_sha256": "c8a1ac469a826ea3547ac220c7bbfdcd6b58080d4ec596ff2a0149c5ccb9b699"
|
|
68
|
+
},
|
|
69
|
+
"leaderboard_get_markets": {
|
|
70
|
+
"schema_sha256": "a317a038e3a1303d3f57f75a48829a6eacdb4de0389b0b2c37f2a0b089079dd7"
|
|
71
|
+
},
|
|
72
|
+
"leaderboard_get_momentum_events": {
|
|
73
|
+
"schema_sha256": "0820afef65df3db8c953d3730ec788582dedc829d4f69ee4cde68911d574fcf5"
|
|
74
|
+
},
|
|
75
|
+
"leaderboard_get_status": {
|
|
76
|
+
"schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
|
|
77
|
+
},
|
|
78
|
+
"leaderboard_get_top": {
|
|
79
|
+
"schema_sha256": "454650ea0b6ef0d3c649572d64181d76c721501c7bfe493807cd217ebb0b959d"
|
|
80
|
+
},
|
|
81
|
+
"leaderboard_get_trader": {
|
|
82
|
+
"schema_sha256": "0a2734aef6353d2453bf6baf1d98adeb1f2a160fbce63100028ecf14653a598b"
|
|
83
|
+
},
|
|
84
|
+
"leaderboard_get_trader_positions": {
|
|
85
|
+
"schema_sha256": "0a2734aef6353d2453bf6baf1d98adeb1f2a160fbce63100028ecf14653a598b"
|
|
86
|
+
},
|
|
87
|
+
"list_senpi_guides": {
|
|
88
|
+
"schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
|
|
89
|
+
},
|
|
90
|
+
"market_get_asset_data": {
|
|
91
|
+
"schema_sha256": "00c3d06ea47ed8d5c15db12cfa1d7cb3c0e13489ed352fb9a0bcab757a654ddb"
|
|
92
|
+
},
|
|
93
|
+
"market_get_cross_asset_flows": {
|
|
94
|
+
"schema_sha256": "64c0acc7ee5112d28137cf5da5b7391e7d46e3059aa9e549dcf8f043524211ba"
|
|
95
|
+
},
|
|
96
|
+
"market_get_funding_history": {
|
|
97
|
+
"schema_sha256": "7ab047ba0d7d08fffe96b677137117584e198c1500e83bd53075a68970638d16"
|
|
98
|
+
},
|
|
99
|
+
"market_get_funding_regime": {
|
|
100
|
+
"schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
|
|
101
|
+
},
|
|
102
|
+
"market_get_prices": {
|
|
103
|
+
"schema_sha256": "31d8841aef56629c14676092d10b023aed8d1ebfc83c35b57709980f1c4d5584"
|
|
104
|
+
},
|
|
105
|
+
"market_list_instruments": {
|
|
106
|
+
"schema_sha256": "6c94db6f450c8cbd6e3977beaddddd9861b2534239d96d1e48949fa749f0a0cd"
|
|
107
|
+
},
|
|
108
|
+
"ratchet_stop_add": {
|
|
109
|
+
"schema_sha256": "40be0af285960b6bccd8ee6be9c59672a9b0f3497b7cdcfaffdcdeea4a8dfea8"
|
|
110
|
+
},
|
|
111
|
+
"ratchet_stop_delete": {
|
|
112
|
+
"schema_sha256": "6691ba26604fa3c87424289cf222af89b4cc8e19faf0c23c51f3dea4d12b3274"
|
|
113
|
+
},
|
|
114
|
+
"ratchet_stop_edit": {
|
|
115
|
+
"schema_sha256": "0f3d557613ce310b9e8029f7a36e3fab8936b41acf2a58963fa6d330105b6204"
|
|
116
|
+
},
|
|
117
|
+
"ratchet_stop_events": {
|
|
118
|
+
"schema_sha256": "2571476a51a089c95f8c3466eb8c6dd7144c1a1735801dbdfdd2dd40b76cf485"
|
|
119
|
+
},
|
|
120
|
+
"ratchet_stop_get": {
|
|
121
|
+
"schema_sha256": "567f87bcc34142ab4408542ed895c6662a7dd1b8d294261a58ef00c05622f2d0"
|
|
122
|
+
},
|
|
123
|
+
"ratchet_stop_list": {
|
|
124
|
+
"schema_sha256": "478226aafa7f1497a6fa29610208cd7a7075cbc75d102801f029ab5064bb456b"
|
|
125
|
+
},
|
|
126
|
+
"read_senpi_guide": {
|
|
127
|
+
"schema_sha256": "7e3d0dda419529f5f4f192a2a87e7897d9b13dda157803276df802240a2961ad"
|
|
128
|
+
},
|
|
129
|
+
"strategy_bridge_funds_from_hyperliquid_to_evm": {
|
|
130
|
+
"schema_sha256": "625d5c98d21eb3d3244f68e75af1496e8dbd58a7bbee5a3c7e4431aef025a645"
|
|
131
|
+
},
|
|
132
|
+
"strategy_close": {
|
|
133
|
+
"schema_sha256": "4dd7cae4b3282cc957341124d010f1f66774d4da56c4bb0ee19697b94f8f6ab1"
|
|
134
|
+
},
|
|
135
|
+
"strategy_close_positions": {
|
|
136
|
+
"schema_sha256": "1f301a7d902a96238e905192b135231e1afbb7236329bbdc3c1d4fc6a33ef58f"
|
|
137
|
+
},
|
|
138
|
+
"strategy_create": {
|
|
139
|
+
"schema_sha256": "8d164f7bc0a350ebfc73cb9708cca0065b4e144e2afec5087dce7bde8e46a099"
|
|
140
|
+
},
|
|
141
|
+
"strategy_create_custom_strategy": {
|
|
142
|
+
"schema_sha256": "10647877b62d274a6196fdd4c5586083706cf077fd398e3e5fc2e4e214952664"
|
|
143
|
+
},
|
|
144
|
+
"strategy_get": {
|
|
145
|
+
"schema_sha256": "85a57eda310b79cb1c6be47a3ae1b9f42d1274abbd6afcde1c2ef55734d2fea6"
|
|
146
|
+
},
|
|
147
|
+
"strategy_get_asset_trading_limits": {
|
|
148
|
+
"schema_sha256": "a7e88d43bcc19f61c9364d8008d14f471f6baf7a0b82a095de982959e70fe53d"
|
|
149
|
+
},
|
|
150
|
+
"strategy_get_clearinghouse_state": {
|
|
151
|
+
"schema_sha256": "06a33161f5e39fb38fbc79db0728b2404c18f0351169f28bf48274f5ea27d44e"
|
|
152
|
+
},
|
|
153
|
+
"strategy_get_open_orders": {
|
|
154
|
+
"schema_sha256": "23e715d14e321fe64647512e67a6e0553179268fb6e4dc17aaff4da8b1efed17"
|
|
155
|
+
},
|
|
156
|
+
"strategy_get_pnl_and_account_value_history": {
|
|
157
|
+
"schema_sha256": "0198d02c8c07affda424afe6f761d2c4525a2f66d1b441c5bdeeb56a2b992877"
|
|
158
|
+
},
|
|
159
|
+
"strategy_list": {
|
|
160
|
+
"schema_sha256": "78b1037a28cf446ad73cee48cb55d39a917c47fb0cc363c0836110039cf72a9f"
|
|
161
|
+
},
|
|
162
|
+
"strategy_pause": {
|
|
163
|
+
"schema_sha256": "4dd7cae4b3282cc957341124d010f1f66774d4da56c4bb0ee19697b94f8f6ab1"
|
|
164
|
+
},
|
|
165
|
+
"strategy_top_up": {
|
|
166
|
+
"schema_sha256": "5b90a94f30311ccf4ab6fd207929fef356073783acce189765cf94e5245f6a83"
|
|
167
|
+
},
|
|
168
|
+
"strategy_update": {
|
|
169
|
+
"schema_sha256": "7011937857282acf33e08f1d51c4d4425d325a48d5f7fc52bc580ebce1c52093"
|
|
170
|
+
},
|
|
171
|
+
"strategy_withdraw_funds": {
|
|
172
|
+
"schema_sha256": "297b1e9fb90acc7d62d2e1d95e6cb60a716f1cf6299e9ea6bf4ba3dd47387631"
|
|
173
|
+
},
|
|
174
|
+
"transfer_spot_to_perps": {
|
|
175
|
+
"schema_sha256": "89a78a64ef01c8ceaa381e54158c3111fa1671c98daea6a72e115a2a028568bf"
|
|
176
|
+
},
|
|
177
|
+
"user_claim_referral_rewards": {
|
|
178
|
+
"schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
|
|
179
|
+
},
|
|
180
|
+
"user_get_me": {
|
|
181
|
+
"schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
|
|
182
|
+
},
|
|
183
|
+
"user_get_referral_rewards": {
|
|
184
|
+
"schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
|
|
185
|
+
},
|
|
186
|
+
"user_get_senpi_points": {
|
|
187
|
+
"schema_sha256": "09aae7dac73afe34d62721e3c59c14cc2e78cdba77f8bee971161a7841379925"
|
|
188
|
+
},
|
|
189
|
+
"user_get_senpi_points_leaderboard": {
|
|
190
|
+
"schema_sha256": "e7289cdc922d69fd9028e0ddf3b9f848424a0bab059d79865a7382cb46dde444"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|