@elaraai/create-e3 1.0.18 → 1.0.19
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/index.js +46 -3
- package/package.json +1 -1
- package/templates/e3/README.md +5 -1
- package/templates/e3/gitignore +2 -0
- package/templates/e3/platform_module/__init__.py +16 -0
- package/templates/e3/platform_module/example.py +31 -0
- package/templates/e3/pyproject.platform.toml +28 -0
- package/templates/e3/src/index.platform.py.ts +58 -0
- package/templates/e3/src/index.platform.ts +39 -0
- package/templates/e3/src/index.ui.platform.py.ts +59 -0
- package/templates/e3/src/index.ui.platform.ts +40 -0
- package/templates/e3/src/index.ui.ts +1 -1
- package/templates/e3/src/platform/example.ts +16 -0
- package/templates/e3/src/platform/index.ts +14 -0
- package/templates/e3/src/platform_module.ts +13 -0
- package/templates/e3/src/{surface.tsx → ui/index.tsx} +3 -1
- package/templates/e3/template.json +35 -3
- package/templates/e3/tests/test_unit.py +9 -0
package/dist/index.js
CHANGED
|
@@ -43,6 +43,18 @@ var MANIFEST_FILE = "template.json";
|
|
|
43
43
|
function substituteTokens(content, names) {
|
|
44
44
|
return content.replaceAll("__PROJECT_NAME__", names.projectName).replaceAll("__DISPLAY_NAME__", names.displayName).replaceAll("__WORKSPACE_NAME__", names.workspaceName);
|
|
45
45
|
}
|
|
46
|
+
function isPlainObject(v) {
|
|
47
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
48
|
+
}
|
|
49
|
+
function mergeInto(target, source) {
|
|
50
|
+
for (const [k, v] of Object.entries(source)) {
|
|
51
|
+
const existing = target[k];
|
|
52
|
+
if (isPlainObject(existing) && isPlainObject(v))
|
|
53
|
+
mergeInto(existing, v);
|
|
54
|
+
else
|
|
55
|
+
target[k] = v;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
46
58
|
function transformPackageJson(raw, names, version, manifest, enabled) {
|
|
47
59
|
const pkg = JSON.parse(raw);
|
|
48
60
|
pkg.name = `@elaraai/${names.projectName}`;
|
|
@@ -70,6 +82,10 @@ function transformPackageJson(raw, names, version, manifest, enabled) {
|
|
|
70
82
|
else
|
|
71
83
|
delete scripts?.[name];
|
|
72
84
|
}
|
|
85
|
+
for (const [feature, spec] of Object.entries(manifest.features)) {
|
|
86
|
+
if (enabled(feature) && spec.packageJson)
|
|
87
|
+
mergeInto(pkg, spec.packageJson);
|
|
88
|
+
}
|
|
73
89
|
}
|
|
74
90
|
const pin = `^${version}`;
|
|
75
91
|
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
@@ -114,7 +130,12 @@ function scaffold(options) {
|
|
|
114
130
|
throw new Error(`Template directory not found: ${templateDir}`);
|
|
115
131
|
}
|
|
116
132
|
const manifest = loadManifest(templateDir);
|
|
117
|
-
const enabled = (feature) =>
|
|
133
|
+
const enabled = (feature) => {
|
|
134
|
+
const spec = manifest?.features[feature];
|
|
135
|
+
if (spec?.allOf)
|
|
136
|
+
return spec.allOf.every(enabled);
|
|
137
|
+
return options.features?.[feature] ?? spec?.default ?? true;
|
|
138
|
+
};
|
|
118
139
|
const skip = /* @__PURE__ */ new Set();
|
|
119
140
|
const renames = {};
|
|
120
141
|
if (manifest) {
|
|
@@ -129,6 +150,17 @@ function scaffold(options) {
|
|
|
129
150
|
skip.add(f);
|
|
130
151
|
}
|
|
131
152
|
}
|
|
153
|
+
const variants = manifest.indexVariants ?? [];
|
|
154
|
+
if (variants.length > 0) {
|
|
155
|
+
const dest = (variants.find((v) => v.when.length === 0) ?? variants[variants.length - 1]).source;
|
|
156
|
+
const winner = variants.find((v) => v.when.every(enabled)) ?? variants[variants.length - 1];
|
|
157
|
+
for (const v of variants) {
|
|
158
|
+
if (v.source !== winner.source)
|
|
159
|
+
skip.add(v.source);
|
|
160
|
+
}
|
|
161
|
+
if (winner.source !== dest)
|
|
162
|
+
renames[winner.source] = dest;
|
|
163
|
+
}
|
|
132
164
|
}
|
|
133
165
|
const names = deriveNames(name, cwd);
|
|
134
166
|
const inPlace = name === ".";
|
|
@@ -219,12 +251,15 @@ async function resolveFeatures(templateDir, args) {
|
|
|
219
251
|
return {};
|
|
220
252
|
const manifest = JSON.parse(readFileSync2(manifestPath, "utf8"));
|
|
221
253
|
const features = {};
|
|
222
|
-
for (const [key, spec] of Object.entries(manifest.features))
|
|
254
|
+
for (const [key, spec] of Object.entries(manifest.features)) {
|
|
255
|
+
if (spec.allOf)
|
|
256
|
+
continue;
|
|
223
257
|
features[key] = spec.default ?? true;
|
|
258
|
+
}
|
|
224
259
|
const runnerKeys = Object.keys(manifest.features).filter((k) => k.startsWith("runner:"));
|
|
225
260
|
const runnerName = (key) => key.slice("runner:".length);
|
|
226
261
|
const runnersFlag = args.find((a) => a.startsWith("--runners="));
|
|
227
|
-
const selectionFlags = ["--tests", "--no-tests", "--ui", "--no-ui", "--eslint", "--no-eslint"].some((f) => args.includes(f)) || Boolean(runnersFlag);
|
|
262
|
+
const selectionFlags = ["--tests", "--no-tests", "--ui", "--no-ui", "--platform", "--no-platform", "--eslint", "--no-eslint"].some((f) => args.includes(f)) || Boolean(runnersFlag);
|
|
228
263
|
if (args.includes("--tests"))
|
|
229
264
|
features["tests"] = true;
|
|
230
265
|
if (args.includes("--no-tests"))
|
|
@@ -233,6 +268,10 @@ async function resolveFeatures(templateDir, args) {
|
|
|
233
268
|
features["ui"] = true;
|
|
234
269
|
if (args.includes("--no-ui"))
|
|
235
270
|
features["ui"] = false;
|
|
271
|
+
if (args.includes("--platform"))
|
|
272
|
+
features["platform"] = true;
|
|
273
|
+
if (args.includes("--no-platform"))
|
|
274
|
+
features["platform"] = false;
|
|
236
275
|
if (args.includes("--eslint"))
|
|
237
276
|
features["eslint"] = true;
|
|
238
277
|
if (args.includes("--no-eslint"))
|
|
@@ -264,6 +303,9 @@ async function resolveFeatures(templateDir, args) {
|
|
|
264
303
|
for (const key of runnerKeys)
|
|
265
304
|
features[key] = picks.has(runnerName(key));
|
|
266
305
|
}
|
|
306
|
+
if ("platform" in manifest.features) {
|
|
307
|
+
features["platform"] = await askYesNo(rl, "Include a project-owned platform module (custom TS-East functions, plus Python when east-py is on)?", features["platform"]);
|
|
308
|
+
}
|
|
267
309
|
if ("eslint" in manifest.features) {
|
|
268
310
|
features["eslint"] = await askYesNo(rl, "Include ESLint with the East lint rules?", features["eslint"]);
|
|
269
311
|
}
|
|
@@ -292,6 +334,7 @@ function printHelp(kind) {
|
|
|
292
334
|
if (kind === "e3") {
|
|
293
335
|
console.log(" --tests | --no-tests include test files (default: yes)");
|
|
294
336
|
console.log(" --ui | --no-ui include east-ui + e3-ui UI components (default: no)");
|
|
337
|
+
console.log(" --platform | --no-platform include a project-owned platform module (TS-East; +Python when east-py is on) (default: no)");
|
|
295
338
|
console.log(" --runners=east-node,east-c,east-py East runtimes to include (default: all)");
|
|
296
339
|
}
|
|
297
340
|
console.log("");
|
package/package.json
CHANGED
package/templates/e3/README.md
CHANGED
|
@@ -40,5 +40,9 @@ npm run lint # lint sources
|
|
|
40
40
|
npm run clean # remove build output, dependencies, and the local repo
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
If you scaffolded with UI, `src/
|
|
43
|
+
If you scaffolded with UI, `src/ui/index.tsx` holds a decision surface — a `ui()` task an operator
|
|
44
44
|
uses to observe and act on the recommendation, registered in the package next to the decision.
|
|
45
|
+
|
|
46
|
+
If you scaffolded with `--platform`, `src/platform/` holds project-owned TS-East platform functions
|
|
47
|
+
(exported via `./platform`) and `platform_module/` holds the Python ones — replace the generated
|
|
48
|
+
`example` functions with your own native code, called from tasks like any East function.
|
package/templates/e3/gitignore
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Project-owned Python platform functions, aggregated for the east-py runner.
|
|
2
|
+
|
|
3
|
+
`east-py run -p platform_module` imports this package and reads the top-level
|
|
4
|
+
``platform`` list below. This mirrors how the first-party east-py-std /
|
|
5
|
+
east-py-datascience packages are built: each submodule ends with
|
|
6
|
+
``<name>_impl = platform_functions(__name__)``, and this file spreads them all
|
|
7
|
+
into ``platform``.
|
|
8
|
+
|
|
9
|
+
To add a function: create a module beside this file (e.g. ``pricing.py``) ending
|
|
10
|
+
with ``pricing_impl = platform_functions(__name__)``, then add an import and
|
|
11
|
+
spread it into ``platform`` below.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .example import example_impl
|
|
15
|
+
|
|
16
|
+
platform = [*example_impl]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""An example project-owned Python platform function — replace it with your own.
|
|
2
|
+
|
|
3
|
+
Platform functions let East call NATIVE Python (numpy, pandas, scikit-learn, …)
|
|
4
|
+
that East itself can't express. The ``@platform_function`` is bound to East by
|
|
5
|
+
its dotted ``"<project>.<fn>"`` name, which the TypeScript declaration in
|
|
6
|
+
``src/platform_module.ts`` mirrors exactly — keep the two in lockstep. Add
|
|
7
|
+
native dependencies to ``pyproject.toml`` and import them inside the body.
|
|
8
|
+
|
|
9
|
+
This module ends by collecting its functions into ``example_impl`` (the
|
|
10
|
+
canonical east-py idiom — ``platform_functions`` keys the registry on each
|
|
11
|
+
function's ``__module__``), which ``__init__.py`` spreads into the package's
|
|
12
|
+
top-level ``platform`` list.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from east.runtime.platform import platform_function, platform_functions
|
|
16
|
+
from east.types.types import ArrayType, FloatType
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@platform_function(
|
|
20
|
+
inputs=[ArrayType(FloatType)],
|
|
21
|
+
output=FloatType,
|
|
22
|
+
name="__PROJECT_NAME__.example_python",
|
|
23
|
+
)
|
|
24
|
+
def example_python(values):
|
|
25
|
+
"""Example: the mean of a list of floats. Replace with your own logic."""
|
|
26
|
+
values = list(values)
|
|
27
|
+
return sum(values) / len(values) if values else 0.0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# The platform functions defined in THIS module, in definition order.
|
|
31
|
+
example_impl = platform_functions(__name__)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "__PROJECT_NAME__"
|
|
3
|
+
description = "__DISPLAY_NAME__"
|
|
4
|
+
requires-python = ">=3.11"
|
|
5
|
+
version = "0.1.0"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"elaraai-east-py",
|
|
8
|
+
"elaraai-east-py-std",
|
|
9
|
+
"elaraai-east-py-io",
|
|
10
|
+
"elaraai-east-py-datascience",
|
|
11
|
+
"elaraai-east-py-cli",
|
|
12
|
+
"pytest",
|
|
13
|
+
"pytest-subtests",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
# Packaging block — without it `uv sync` would not install this project's own
|
|
17
|
+
# code, so `east-py run -p platform_module` could not import it. Matches the
|
|
18
|
+
# repo's pure-Python packages (east-py-std/io/cli). This variant of
|
|
19
|
+
# pyproject.toml ships only with the `--platform` scaffold feature (which
|
|
20
|
+
# requires the east-py runner); the plain pyproject.toml has no build-system, so
|
|
21
|
+
# `uv sync` installs dependencies without trying to build a package that isn't
|
|
22
|
+
# there.
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["setuptools>=61"]
|
|
25
|
+
build-backend = "setuptools.build_meta"
|
|
26
|
+
|
|
27
|
+
[tool.setuptools]
|
|
28
|
+
packages = ["platform_module"]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import e3 from "@elaraai/e3";
|
|
2
|
+
import { East, ArrayType, FloatType, IntegerType } from "@elaraai/east";
|
|
3
|
+
|
|
4
|
+
import { examplePython } from "./platform_module.js";
|
|
5
|
+
import { exampleNode } from "./platform/index.js";
|
|
6
|
+
|
|
7
|
+
// In East a decision is a typed task over inputs. This one recommends how many
|
|
8
|
+
// units to reorder to bring stock up to its target level — never negative.
|
|
9
|
+
export const onHandInput = e3.input("on_hand", IntegerType, 12n);
|
|
10
|
+
export const targetInput = e3.input("reorder_to", IntegerType, 50n);
|
|
11
|
+
|
|
12
|
+
export const reorderFn = East.function(
|
|
13
|
+
[IntegerType, IntegerType],
|
|
14
|
+
IntegerType,
|
|
15
|
+
($, onHand, target) => {
|
|
16
|
+
const gap = $.let(target.subtract(onHand));
|
|
17
|
+
$.return(East.greater(gap, 0n).ifElse(() => gap, () => 0n));
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const reorderQty = e3.task("reorder_qty", [onHandInput, targetInput], reorderFn);
|
|
22
|
+
|
|
23
|
+
// An example project-owned PYTHON platform function on the east-py runtime.
|
|
24
|
+
// `{ custom: "platform_module" }` loads the project's own package, resolved from
|
|
25
|
+
// <project>/.venv after `uv sync`. Replace `example_python`
|
|
26
|
+
// (platform_module/example.py) with your own.
|
|
27
|
+
export const exampleValuesInput = e3.input("example_values", ArrayType(FloatType), [1.0, 2.0, 3.0, 4.0, 5.0]);
|
|
28
|
+
|
|
29
|
+
export const examplePythonFn = East.function(
|
|
30
|
+
[ArrayType(FloatType)],
|
|
31
|
+
FloatType,
|
|
32
|
+
($, values) => {
|
|
33
|
+
$.return(examplePython(values));
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
export const examplePythonTask = e3.task("example_python", [exampleValuesInput], examplePythonFn, {
|
|
38
|
+
runner: { runtime: "east-py", platforms: [{ custom: "platform_module" }, "east-py-std"] },
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// An example project-owned TS-East platform function on the east-node runtime.
|
|
42
|
+
// `{ custom: "@elaraai/__PROJECT_NAME__" }` loads this package's own `./platform`
|
|
43
|
+
// export. Replace `exampleNode` (src/platform/example.ts) with your own.
|
|
44
|
+
export const exampleFactorInput = e3.input("example_factor", FloatType, 1.5);
|
|
45
|
+
|
|
46
|
+
export const exampleNodeFn = East.function(
|
|
47
|
+
[IntegerType, FloatType],
|
|
48
|
+
IntegerType,
|
|
49
|
+
($, value, factor) => {
|
|
50
|
+
$.return(exampleNode(value, factor));
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
export const exampleNodeTask = e3.task("example_node", [reorderQty.output, exampleFactorInput], exampleNodeFn, {
|
|
55
|
+
runner: { runtime: "east-node", platforms: [{ custom: "@elaraai/__PROJECT_NAME__" }] },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export default e3.package("__PROJECT_NAME__", "1.0.0", reorderQty, examplePythonTask, exampleNodeTask);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import e3 from "@elaraai/e3";
|
|
2
|
+
import { East, FloatType, IntegerType } from "@elaraai/east";
|
|
3
|
+
|
|
4
|
+
import { exampleNode } from "./platform/index.js";
|
|
5
|
+
|
|
6
|
+
// In East a decision is a typed task over inputs. This one recommends how many
|
|
7
|
+
// units to reorder to bring stock up to its target level — never negative.
|
|
8
|
+
export const onHandInput = e3.input("on_hand", IntegerType, 12n);
|
|
9
|
+
export const targetInput = e3.input("reorder_to", IntegerType, 50n);
|
|
10
|
+
|
|
11
|
+
export const reorderFn = East.function(
|
|
12
|
+
[IntegerType, IntegerType],
|
|
13
|
+
IntegerType,
|
|
14
|
+
($, onHand, target) => {
|
|
15
|
+
const gap = $.let(target.subtract(onHand));
|
|
16
|
+
$.return(East.greater(gap, 0n).ifElse(() => gap, () => 0n));
|
|
17
|
+
},
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export const reorderQty = e3.task("reorder_qty", [onHandInput, targetInput], reorderFn);
|
|
21
|
+
|
|
22
|
+
// An example project-owned TS-East platform function on the east-node runtime.
|
|
23
|
+
// `{ custom: "@elaraai/__PROJECT_NAME__" }` loads this package's own `./platform`
|
|
24
|
+
// export. Replace `exampleNode` (src/platform/example.ts) with your own.
|
|
25
|
+
export const exampleFactorInput = e3.input("example_factor", FloatType, 1.5);
|
|
26
|
+
|
|
27
|
+
export const exampleNodeFn = East.function(
|
|
28
|
+
[IntegerType, FloatType],
|
|
29
|
+
IntegerType,
|
|
30
|
+
($, value, factor) => {
|
|
31
|
+
$.return(exampleNode(value, factor));
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export const exampleNodeTask = e3.task("example_node", [reorderQty.output, exampleFactorInput], exampleNodeFn, {
|
|
36
|
+
runner: { runtime: "east-node", platforms: [{ custom: "@elaraai/__PROJECT_NAME__" }] },
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export default e3.package("__PROJECT_NAME__", "1.0.0", reorderQty, exampleNodeTask);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import e3 from "@elaraai/e3";
|
|
2
|
+
import { East, ArrayType, FloatType, IntegerType } from "@elaraai/east";
|
|
3
|
+
|
|
4
|
+
import { surface } from "./ui/index.js";
|
|
5
|
+
import { examplePython } from "./platform_module.js";
|
|
6
|
+
import { exampleNode } from "./platform/index.js";
|
|
7
|
+
|
|
8
|
+
// In East a decision is a typed task over inputs. This one recommends how many
|
|
9
|
+
// units to reorder to bring stock up to its target level — never negative.
|
|
10
|
+
export const onHandInput = e3.input("on_hand", IntegerType, 12n);
|
|
11
|
+
export const targetInput = e3.input("reorder_to", IntegerType, 50n);
|
|
12
|
+
|
|
13
|
+
export const reorderFn = East.function(
|
|
14
|
+
[IntegerType, IntegerType],
|
|
15
|
+
IntegerType,
|
|
16
|
+
($, onHand, target) => {
|
|
17
|
+
const gap = $.let(target.subtract(onHand));
|
|
18
|
+
$.return(East.greater(gap, 0n).ifElse(() => gap, () => 0n));
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const reorderQty = e3.task("reorder_qty", [onHandInput, targetInput], reorderFn);
|
|
23
|
+
|
|
24
|
+
// An example project-owned PYTHON platform function on the east-py runtime.
|
|
25
|
+
// `{ custom: "platform_module" }` loads the project's own package, resolved from
|
|
26
|
+
// <project>/.venv after `uv sync`. Replace `example_python`
|
|
27
|
+
// (platform_module/example.py) with your own.
|
|
28
|
+
export const exampleValuesInput = e3.input("example_values", ArrayType(FloatType), [1.0, 2.0, 3.0, 4.0, 5.0]);
|
|
29
|
+
|
|
30
|
+
export const examplePythonFn = East.function(
|
|
31
|
+
[ArrayType(FloatType)],
|
|
32
|
+
FloatType,
|
|
33
|
+
($, values) => {
|
|
34
|
+
$.return(examplePython(values));
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export const examplePythonTask = e3.task("example_python", [exampleValuesInput], examplePythonFn, {
|
|
39
|
+
runner: { runtime: "east-py", platforms: [{ custom: "platform_module" }, "east-py-std"] },
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// An example project-owned TS-East platform function on the east-node runtime.
|
|
43
|
+
// `{ custom: "@elaraai/__PROJECT_NAME__" }` loads this package's own `./platform`
|
|
44
|
+
// export. Replace `exampleNode` (src/platform/example.ts) with your own.
|
|
45
|
+
export const exampleFactorInput = e3.input("example_factor", FloatType, 1.5);
|
|
46
|
+
|
|
47
|
+
export const exampleNodeFn = East.function(
|
|
48
|
+
[IntegerType, FloatType],
|
|
49
|
+
IntegerType,
|
|
50
|
+
($, value, factor) => {
|
|
51
|
+
$.return(exampleNode(value, factor));
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export const exampleNodeTask = e3.task("example_node", [reorderQty.output, exampleFactorInput], exampleNodeFn, {
|
|
56
|
+
runner: { runtime: "east-node", platforms: [{ custom: "@elaraai/__PROJECT_NAME__" }] },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export default e3.package("__PROJECT_NAME__", "1.0.0", reorderQty, examplePythonTask, exampleNodeTask, surface);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import e3 from "@elaraai/e3";
|
|
2
|
+
import { East, FloatType, IntegerType } from "@elaraai/east";
|
|
3
|
+
|
|
4
|
+
import { surface } from "./ui/index.js";
|
|
5
|
+
import { exampleNode } from "./platform/index.js";
|
|
6
|
+
|
|
7
|
+
// In East a decision is a typed task over inputs. This one recommends how many
|
|
8
|
+
// units to reorder to bring stock up to its target level — never negative.
|
|
9
|
+
export const onHandInput = e3.input("on_hand", IntegerType, 12n);
|
|
10
|
+
export const targetInput = e3.input("reorder_to", IntegerType, 50n);
|
|
11
|
+
|
|
12
|
+
export const reorderFn = East.function(
|
|
13
|
+
[IntegerType, IntegerType],
|
|
14
|
+
IntegerType,
|
|
15
|
+
($, onHand, target) => {
|
|
16
|
+
const gap = $.let(target.subtract(onHand));
|
|
17
|
+
$.return(East.greater(gap, 0n).ifElse(() => gap, () => 0n));
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const reorderQty = e3.task("reorder_qty", [onHandInput, targetInput], reorderFn);
|
|
22
|
+
|
|
23
|
+
// An example project-owned TS-East platform function on the east-node runtime.
|
|
24
|
+
// `{ custom: "@elaraai/__PROJECT_NAME__" }` loads this package's own `./platform`
|
|
25
|
+
// export. Replace `exampleNode` (src/platform/example.ts) with your own.
|
|
26
|
+
export const exampleFactorInput = e3.input("example_factor", FloatType, 1.5);
|
|
27
|
+
|
|
28
|
+
export const exampleNodeFn = East.function(
|
|
29
|
+
[IntegerType, FloatType],
|
|
30
|
+
IntegerType,
|
|
31
|
+
($, value, factor) => {
|
|
32
|
+
$.return(exampleNode(value, factor));
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export const exampleNodeTask = e3.task("example_node", [reorderQty.output, exampleFactorInput], exampleNodeFn, {
|
|
37
|
+
runner: { runtime: "east-node", platforms: [{ custom: "@elaraai/__PROJECT_NAME__" }] },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export default e3.package("__PROJECT_NAME__", "1.0.0", reorderQty, exampleNodeTask, surface);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import e3 from "@elaraai/e3";
|
|
2
2
|
import { East, IntegerType } from "@elaraai/east";
|
|
3
3
|
|
|
4
|
-
import { surface } from "./
|
|
4
|
+
import { surface } from "./ui/index.js";
|
|
5
5
|
|
|
6
6
|
// In East a decision is a typed task over inputs. This one recommends how many
|
|
7
7
|
// units to reorder to bring stock up to its target level — never negative.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { East, IntegerType, FloatType } from "@elaraai/east";
|
|
2
|
+
|
|
3
|
+
// An example project-owned TS-East platform function — replace it with your own.
|
|
4
|
+
// Platform functions let East call native TS/Node code East can't express. The
|
|
5
|
+
// declaration (`exampleNode`) is what East code calls in a task; the
|
|
6
|
+
// implementation runs on the east-node runtime. To add another, create a sibling
|
|
7
|
+
// file in this folder and wire it into ./index.ts.
|
|
8
|
+
export const exampleNode = East.platform(
|
|
9
|
+
"__PROJECT_NAME__.example_node",
|
|
10
|
+
[IntegerType, FloatType],
|
|
11
|
+
IntegerType,
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export const exampleNodeImpl = exampleNode.implement(
|
|
15
|
+
(value: bigint, factor: number): bigint => BigInt(Math.ceil(Number(value) * factor)),
|
|
16
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Barrel for the project's TS-East platform functions (run on the east-node
|
|
2
|
+
// runtime). This module IS the package's `./platform` subpath export — its
|
|
3
|
+
// default export is the PlatformFunction[] that east-node-cli loads.
|
|
4
|
+
//
|
|
5
|
+
// To add a function: create a sibling file (e.g. ./discount.ts) exporting its
|
|
6
|
+
// declaration + `<name>Impl`, then add one re-export line and one array entry
|
|
7
|
+
// below.
|
|
8
|
+
import { exampleNodeImpl } from "./example.js";
|
|
9
|
+
|
|
10
|
+
// Re-export each declaration so e3 tasks (src/index.ts) can call them.
|
|
11
|
+
export { exampleNode } from "./example.js";
|
|
12
|
+
|
|
13
|
+
// The PlatformFunction[] the east-node runner loads via `./platform`.
|
|
14
|
+
export default [exampleNodeImpl];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { East, ArrayType, FloatType } from "@elaraai/east";
|
|
2
|
+
|
|
3
|
+
// Type-safe declaration mirroring `platform_module/example.py`'s
|
|
4
|
+
// `example_python` — same dotted "<project>.<fn>" name, same signature.
|
|
5
|
+
// Hand-written (no codegen): keep it in lockstep with the Python
|
|
6
|
+
// @platform_function. East code calls this; the impl runs on the east-py
|
|
7
|
+
// runtime, resolved from the project's own `.venv` (see the `example_python`
|
|
8
|
+
// task in the index).
|
|
9
|
+
export const examplePython = East.platform(
|
|
10
|
+
"__PROJECT_NAME__.example_python",
|
|
11
|
+
[ArrayType(FloatType)],
|
|
12
|
+
FloatType,
|
|
13
|
+
);
|
|
@@ -4,7 +4,9 @@ import { Box, Text, UIComponentType } from "@elaraai/east-ui";
|
|
|
4
4
|
|
|
5
5
|
// A decision surface for an operator to observe and act on the recommendation.
|
|
6
6
|
// east-ui / e3-ui JSX authoring is still being finalised — build this out: bind
|
|
7
|
-
//
|
|
7
|
+
// the tasks and inputs with Data.bind, then add the Observe / Decide controls.
|
|
8
|
+
// To add another surface, define it beside this file and register it in the
|
|
9
|
+
// package (src/index.ts).
|
|
8
10
|
export const surface = ui(
|
|
9
11
|
"surface",
|
|
10
12
|
[],
|
|
@@ -7,11 +7,31 @@
|
|
|
7
7
|
},
|
|
8
8
|
"ui": {
|
|
9
9
|
"default": false,
|
|
10
|
-
"files": ["src/
|
|
11
|
-
"disable": ["src/index.ts"],
|
|
12
|
-
"rename": { "src/index.ui.ts": "src/index.ts" },
|
|
10
|
+
"files": ["src/ui/index.tsx"],
|
|
13
11
|
"dependencies": ["@elaraai/east-ui", "@elaraai/e3-ui"]
|
|
14
12
|
},
|
|
13
|
+
"platform": {
|
|
14
|
+
"default": false,
|
|
15
|
+
"files": ["src/platform/index.ts", "src/platform/example.ts"],
|
|
16
|
+
"packageJson": {
|
|
17
|
+
"exports": {
|
|
18
|
+
".": "./dist/index.js",
|
|
19
|
+
"./platform": "./dist/platform/index.js",
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"platform-py": {
|
|
25
|
+
"allOf": ["platform", "runner:east-py"],
|
|
26
|
+
"files": [
|
|
27
|
+
"platform_module/__init__.py",
|
|
28
|
+
"platform_module/example.py",
|
|
29
|
+
"src/platform_module.ts",
|
|
30
|
+
"pyproject.platform.toml"
|
|
31
|
+
],
|
|
32
|
+
"disable": ["pyproject.toml"],
|
|
33
|
+
"rename": { "pyproject.platform.toml": "pyproject.toml" }
|
|
34
|
+
},
|
|
15
35
|
"runner:east-node": {
|
|
16
36
|
"default": true,
|
|
17
37
|
"devDependencies": ["@elaraai/east-node-cli"]
|
|
@@ -42,6 +62,14 @@
|
|
|
42
62
|
"devDependencies": ["@elaraai/tsserver-plugin-east"]
|
|
43
63
|
}
|
|
44
64
|
},
|
|
65
|
+
"indexVariants": [
|
|
66
|
+
{ "when": ["ui", "platform", "runner:east-py"], "source": "src/index.ui.platform.py.ts" },
|
|
67
|
+
{ "when": ["ui", "platform"], "source": "src/index.ui.platform.ts" },
|
|
68
|
+
{ "when": ["ui"], "source": "src/index.ui.ts" },
|
|
69
|
+
{ "when": ["platform", "runner:east-py"], "source": "src/index.platform.py.ts" },
|
|
70
|
+
{ "when": ["platform"], "source": "src/index.platform.ts" },
|
|
71
|
+
{ "when": [], "source": "src/index.ts" }
|
|
72
|
+
],
|
|
45
73
|
"scriptVariants": {
|
|
46
74
|
"test": [
|
|
47
75
|
{ "when": ["tests", "runner:east-py"], "value": "npm run build && npm run test:export && npm run test:py" },
|
|
@@ -50,6 +78,10 @@
|
|
|
50
78
|
"setup": [
|
|
51
79
|
{ "when": ["runner:east-py"], "value": "npm install && uv sync" },
|
|
52
80
|
{ "when": [], "value": "npm install" }
|
|
81
|
+
],
|
|
82
|
+
"start": [
|
|
83
|
+
{ "when": ["platform"], "value": "npm run build && npm run deploy && e3 dataflow run .repos __WORKSPACE_NAME__" },
|
|
84
|
+
{ "when": [], "value": "npm run deploy && e3 dataflow run .repos __WORKSPACE_NAME__" }
|
|
53
85
|
]
|
|
54
86
|
}
|
|
55
87
|
}
|
|
@@ -21,6 +21,13 @@ try:
|
|
|
21
21
|
except ImportError:
|
|
22
22
|
io_platform = []
|
|
23
23
|
|
|
24
|
+
# A project-owned platform module (scaffolded by `--platform`). Absent unless
|
|
25
|
+
# that feature is on, so this import is best-effort.
|
|
26
|
+
try:
|
|
27
|
+
from platform_module import platform as project_platform
|
|
28
|
+
except ImportError:
|
|
29
|
+
project_platform = []
|
|
30
|
+
|
|
24
31
|
TEST_IR_DIR = Path("dist/test-ir")
|
|
25
32
|
|
|
26
33
|
|
|
@@ -65,6 +72,8 @@ def run_one(ir_file: Path) -> tuple[int, int]:
|
|
|
65
72
|
pf for pf in std_platform if pf["name"] not in test_names
|
|
66
73
|
] + [
|
|
67
74
|
pf for pf in io_platform if pf["name"] not in test_names
|
|
75
|
+
] + [
|
|
76
|
+
pf for pf in project_platform if pf["name"] not in test_names
|
|
68
77
|
] + [
|
|
69
78
|
PlatformFunction(name="describe", inputs=[StringType, FunctionType([], NullType)], output=NullType, type="sync", fn=describe_impl),
|
|
70
79
|
PlatformFunction(name="test", inputs=[StringType, FunctionType([], NullType)], output=NullType, type="sync", fn=test_impl),
|