@styx-api/core 0.5.2 → 0.6.0
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.cjs +88 -13
- package/dist/index.d.cts +47 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +47 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +88 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/backend/python/emit.ts +8 -2
- package/src/backend/python/python.ts +102 -2
- package/src/backend/python/snippet.ts +54 -6
- package/src/backend/snippet-core.ts +32 -3
- package/src/backend/typescript/emit.ts +8 -2
package/dist/index.mjs
CHANGED
|
@@ -3781,7 +3781,8 @@ function emitMetadata$1(ctx, metaConst, cb) {
|
|
|
3781
3781
|
cb.line(`name=${pyStr(name)},`);
|
|
3782
3782
|
cb.line(`package=${pyStr(pkg)},`);
|
|
3783
3783
|
if (ctx.app?.doc?.literature?.length) cb.line(`citations=[${ctx.app.doc.literature.map(pyStr).join(", ")}],`);
|
|
3784
|
-
|
|
3784
|
+
const containerImage = ctx.package?.docker ?? ctx.app?.container?.image;
|
|
3785
|
+
if (containerImage) cb.line(`container_image_tag=${pyStr(containerImage)},`);
|
|
3785
3786
|
});
|
|
3786
3787
|
cb.line(")");
|
|
3787
3788
|
}
|
|
@@ -5399,7 +5400,29 @@ function buildEmitModel(ctx, scope = new Scope(PY_RESERVED)) {
|
|
|
5399
5400
|
names.params = (rootType.kind === "struct" ? namedTypes.get(structKey(rootType)) : void 0) ?? (rootType.kind === "union" ? namedTypes.get(unionKey(rootType)) : void 0) ?? names.params;
|
|
5400
5401
|
const rootTypeTag = appId && pkg ? `${pkg}/${appId}` : void 0;
|
|
5401
5402
|
const paramsType = rootType.kind === "struct" || rootType.kind === "union" ? names.params : mapType$1(rootType, resolveTypeName(namedTypes));
|
|
5403
|
+
const hostName = (childScope, wireKey) => childScope.add(pyScrubIdent(snakeCase(wireKey), PY_RESERVED));
|
|
5404
|
+
const resolve = resolveTypeName(namedTypes);
|
|
5402
5405
|
const sigScope = scope.child(["params", "runner"]);
|
|
5406
|
+
const sigEntries = rootIsStruct && rootType.kind === "struct" ? buildSigEntries(rootType, collectFieldInfo(ctx, rootType), (wireKey) => hostName(sigScope, wireKey), pySigOptions(resolve)) : [];
|
|
5407
|
+
const rootStructKey = rootIsStruct && rootType.kind === "struct" ? structKey(rootType) : void 0;
|
|
5408
|
+
const nestedFactories = [];
|
|
5409
|
+
for (const decl of typeDecls) {
|
|
5410
|
+
if (decl.type.kind !== "struct") continue;
|
|
5411
|
+
const sKey = structKey(decl.type);
|
|
5412
|
+
if (sKey === rootStructKey) continue;
|
|
5413
|
+
const funcName = scope.add(pyScrubIdent(snakeCase(decl.name) + "_params", PY_RESERVED));
|
|
5414
|
+
const atType = decl.type.fields["@type"];
|
|
5415
|
+
const typeTag = atType && atType.kind === "literal" && typeof atType.value === "string" ? atType.value : void 0;
|
|
5416
|
+
const factoryScope = scope.child(["params"]);
|
|
5417
|
+
const factorySig = buildSigEntries(decl.type, collectFieldInfo(ctx, decl.type), (wireKey) => hostName(factoryScope, wireKey), pySigOptions(resolve));
|
|
5418
|
+
nestedFactories.push({
|
|
5419
|
+
typeName: decl.name,
|
|
5420
|
+
structKey: sKey,
|
|
5421
|
+
funcName,
|
|
5422
|
+
typeTag,
|
|
5423
|
+
sigEntries: factorySig
|
|
5424
|
+
});
|
|
5425
|
+
}
|
|
5403
5426
|
return {
|
|
5404
5427
|
appId,
|
|
5405
5428
|
pkg,
|
|
@@ -5410,13 +5433,14 @@ function buildEmitModel(ctx, scope = new Scope(PY_RESERVED)) {
|
|
|
5410
5433
|
typeDecls,
|
|
5411
5434
|
rootTypeTag,
|
|
5412
5435
|
paramsType,
|
|
5413
|
-
sigEntries
|
|
5436
|
+
sigEntries,
|
|
5437
|
+
nestedFactories
|
|
5414
5438
|
};
|
|
5415
5439
|
}
|
|
5416
5440
|
function generatePython(ctx, packageScope) {
|
|
5417
5441
|
const cb = new CodeBuilder(" ");
|
|
5418
5442
|
const scope = packageScope ?? new Scope(PY_RESERVED);
|
|
5419
|
-
const { names, rootType, rootIsStruct, namedTypes, typeDecls, rootTypeTag, paramsType, sigEntries } = buildEmitModel(ctx, scope);
|
|
5443
|
+
const { names, rootType, rootIsStruct, namedTypes, typeDecls, rootTypeTag, paramsType, sigEntries, nestedFactories } = buildEmitModel(ctx, scope);
|
|
5420
5444
|
cb.comment("This file was auto generated by Styx.", "# ");
|
|
5421
5445
|
cb.comment("Do not edit this file directly.", "# ");
|
|
5422
5446
|
cb.blank();
|
|
@@ -5435,6 +5459,10 @@ function generatePython(ctx, packageScope) {
|
|
|
5435
5459
|
emitParamsFactory$1(sigEntries, names.paramsFn, paramsType, rootTypeTag, cb);
|
|
5436
5460
|
cb.blank();
|
|
5437
5461
|
}
|
|
5462
|
+
for (const nf of nestedFactories) {
|
|
5463
|
+
emitParamsFactory$1(nf.sigEntries, nf.funcName, nf.typeName, nf.typeTag, cb);
|
|
5464
|
+
cb.blank();
|
|
5465
|
+
}
|
|
5438
5466
|
emitValidate$1(ctx, rootType, ctx.expr, paramsType, names.validate, resolveTypeName(namedTypes), scope, cb);
|
|
5439
5467
|
cb.blank();
|
|
5440
5468
|
emitBuildCargs$1(ctx, rootType, paramsType, names.cargs, cb);
|
|
@@ -5454,6 +5482,7 @@ function generatePython(ctx, packageScope) {
|
|
|
5454
5482
|
names.cargs,
|
|
5455
5483
|
...[names.outputsFn],
|
|
5456
5484
|
...rootIsStruct ? [names.paramsFn, names.execute] : [],
|
|
5485
|
+
...nestedFactories.map((nf) => nf.funcName),
|
|
5457
5486
|
names.validate,
|
|
5458
5487
|
names.wrapper
|
|
5459
5488
|
];
|
|
@@ -5621,9 +5650,13 @@ function structAtType(type) {
|
|
|
5621
5650
|
}
|
|
5622
5651
|
/**
|
|
5623
5652
|
* Render a struct config as a host object literal (Python dict / TS object).
|
|
5624
|
-
* Keys are the Boutiques wire names (the generated TypedDict / interface keys)
|
|
5625
|
-
*
|
|
5626
|
-
*
|
|
5653
|
+
* Keys are the Boutiques wire names (the generated TypedDict / interface keys).
|
|
5654
|
+
*
|
|
5655
|
+
* When the dialect supplies `structConstructor` (Python, which generates a
|
|
5656
|
+
* per-struct `_params` builder), the struct is rendered as that factory call
|
|
5657
|
+
* instead - e.g. `tool_corrected_output_params(file="...")`. Backends without
|
|
5658
|
+
* per-struct builders (TypeScript) leave the hook unset and build the plain
|
|
5659
|
+
* object literal below.
|
|
5627
5660
|
*
|
|
5628
5661
|
* `@type` is emitted from the struct's literal discriminator field when present
|
|
5629
5662
|
* (union variants carry a required, load-bearing `@type`); for the root call the
|
|
@@ -5633,6 +5666,10 @@ function structAtType(type) {
|
|
|
5633
5666
|
*/
|
|
5634
5667
|
function renderStructLiteral(value, type, indent, d, injectAtType) {
|
|
5635
5668
|
const obj = isRecord(value) ? value : {};
|
|
5669
|
+
if (injectAtType === void 0 && d.structConstructor) {
|
|
5670
|
+
const call = d.structConstructor(type, obj, indent, d);
|
|
5671
|
+
if (call !== void 0) return call;
|
|
5672
|
+
}
|
|
5636
5673
|
const inner = indent + d.indentUnit;
|
|
5637
5674
|
const lines = [];
|
|
5638
5675
|
const atType = structAtType(type) ?? injectAtType;
|
|
@@ -5708,6 +5745,41 @@ const pyDialect = {
|
|
|
5708
5745
|
objKey: (k) => pyStr(k)
|
|
5709
5746
|
};
|
|
5710
5747
|
/**
|
|
5748
|
+
* Build a Python dialect that renders nested structs as `_params` factory calls
|
|
5749
|
+
* (matching the generated code's per-struct builders) rather than plain dicts.
|
|
5750
|
+
* Each factory is looked up by the struct's structural key; field values use the
|
|
5751
|
+
* factory's scrubbed host kwarg names (from the same `buildSigEntries` the
|
|
5752
|
+
* generated factory is built from), and the function name is package-qualified
|
|
5753
|
+
* so the call resolves through `from <root> import <pkg>`. A struct with no
|
|
5754
|
+
* registered factory (shouldn't happen for well-formed nested types) falls back
|
|
5755
|
+
* to the plain dict literal.
|
|
5756
|
+
*/
|
|
5757
|
+
function pyDialectWithFactories(model, pkg) {
|
|
5758
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
5759
|
+
for (const f of model.nestedFactories) byKey.set(f.structKey, {
|
|
5760
|
+
call: pkg ? `${pkg}.${f.funcName}` : f.funcName,
|
|
5761
|
+
nameByWire: new Map(f.sigEntries.map((e) => [e.wireKey, e.name]))
|
|
5762
|
+
});
|
|
5763
|
+
return {
|
|
5764
|
+
...pyDialect,
|
|
5765
|
+
structConstructor(type, value, indent, d) {
|
|
5766
|
+
const entry = byKey.get(structKey(type));
|
|
5767
|
+
if (!entry) return void 0;
|
|
5768
|
+
const inner = indent + d.indentUnit;
|
|
5769
|
+
const lines = [];
|
|
5770
|
+
for (const [wireKey, fieldType] of Object.entries(type.fields)) {
|
|
5771
|
+
if (wireKey === "@type") continue;
|
|
5772
|
+
if (fieldType.kind === "literal") continue;
|
|
5773
|
+
if (!(wireKey in value)) continue;
|
|
5774
|
+
const name = entry.nameByWire.get(wireKey) ?? wireKey;
|
|
5775
|
+
lines.push(`${inner}${name}=${renderValue(value[wireKey], fieldType, inner, d)},`);
|
|
5776
|
+
}
|
|
5777
|
+
if (lines.length === 0) return `${entry.call}()`;
|
|
5778
|
+
return `${entry.call}(\n${lines.join("\n")}\n${indent})`;
|
|
5779
|
+
}
|
|
5780
|
+
};
|
|
5781
|
+
}
|
|
5782
|
+
/**
|
|
5711
5783
|
* Render a Python call snippet for one tool from a config object (the params
|
|
5712
5784
|
* dict the form produces, keyed by Boutiques wire names).
|
|
5713
5785
|
*
|
|
@@ -5716,8 +5788,9 @@ const pyDialect = {
|
|
|
5716
5788
|
* *scrubbed host* identifiers (`float` -> `float_`), not the wire keys; the
|
|
5717
5789
|
* per-field mapping comes from the same `buildSigEntries` the generated wrapper
|
|
5718
5790
|
* is built from, so the snippet matches the real signature. Nested structs /
|
|
5719
|
-
* union variants / lists-of-structs
|
|
5720
|
-
*
|
|
5791
|
+
* union variants / lists-of-structs render as calls to their generated
|
|
5792
|
+
* `_params` factory (`fsl.bet_x_params(...)`), matching the per-struct builders
|
|
5793
|
+
* the Python backend emits.
|
|
5721
5794
|
*
|
|
5722
5795
|
* Union- (or otherwise non-struct-) rooted tools have no kwarg wrapper; the
|
|
5723
5796
|
* single dict-style `<tool>` entry is called with one object-literal argument.
|
|
@@ -5738,21 +5811,22 @@ function renderPythonCall(ctx, config, opts = {}) {
|
|
|
5738
5811
|
const model = buildEmitModel(ctx);
|
|
5739
5812
|
const pkg = ctx.package?.name;
|
|
5740
5813
|
const callee = pkg ? `${pkg}.${model.names.wrapper}` : model.names.wrapper;
|
|
5741
|
-
const
|
|
5814
|
+
const dialect = pyDialectWithFactories(model, pkg);
|
|
5815
|
+
const call = model.rootIsStruct && model.rootType.kind === "struct" ? renderKwargCall(callee, model.sigEntries, model.rootType, config, dialect) : `${callee}(${renderValue(config, model.rootType, "", dialect)})`;
|
|
5742
5816
|
if (opts.includeImport === false || !pkg) return call;
|
|
5743
5817
|
const root = opts.packageRoot ?? ctx.project?.name;
|
|
5744
5818
|
return `${root ? `from ${root} import ${pkg}` : `import ${pkg}`}\n\n${call}`;
|
|
5745
5819
|
}
|
|
5746
5820
|
/** Render `callee(name=value, ...)` for a struct root using scrubbed kwarg names. */
|
|
5747
|
-
function renderKwargCall(callee, sigEntries, rootType, config) {
|
|
5821
|
+
function renderKwargCall(callee, sigEntries, rootType, config, dialect) {
|
|
5748
5822
|
const nameFor = new Map(sigEntries.map((e) => [e.wireKey, e.name]));
|
|
5749
|
-
const indent =
|
|
5823
|
+
const indent = dialect.indentUnit;
|
|
5750
5824
|
const lines = [];
|
|
5751
5825
|
for (const [wireKey, fieldType] of Object.entries(rootType.fields)) {
|
|
5752
5826
|
if (fieldType.kind === "literal") continue;
|
|
5753
5827
|
if (!(wireKey in config)) continue;
|
|
5754
5828
|
const name = nameFor.get(wireKey) ?? wireKey;
|
|
5755
|
-
lines.push(`${indent}${name}=${renderValue(config[wireKey], fieldType, indent,
|
|
5829
|
+
lines.push(`${indent}${name}=${renderValue(config[wireKey], fieldType, indent, dialect)},`);
|
|
5756
5830
|
}
|
|
5757
5831
|
if (lines.length === 0) return `${callee}()`;
|
|
5758
5832
|
return `${callee}(\n${lines.join("\n")}\n)`;
|
|
@@ -6896,7 +6970,8 @@ function emitMetadata(ctx, metaConst, cb) {
|
|
|
6896
6970
|
cb.line(`name: ${JSON.stringify(name)},`);
|
|
6897
6971
|
cb.line(`package: ${JSON.stringify(pkg)},`);
|
|
6898
6972
|
if (ctx.app?.doc?.literature?.length) cb.line(`citations: ${JSON.stringify(ctx.app.doc.literature)},`);
|
|
6899
|
-
|
|
6973
|
+
const containerImage = ctx.package?.docker ?? ctx.app?.container?.image;
|
|
6974
|
+
if (containerImage) cb.line(`container_image_tag: ${JSON.stringify(containerImage)},`);
|
|
6900
6975
|
});
|
|
6901
6976
|
cb.line("};");
|
|
6902
6977
|
}
|