.agents/skills/add-function-body/SKILL.md
Follow the full guide in docs/AddFunctionBody.md.
| Component | File |
|---|---|
| Function body definition | onnx/defs/<domain>/defs.cc (inline with schema) |
| FunctionBuilder utilities | onnx/defs/function.h |
| Function tests | onnx/test/cpp/function_get_test.cc, onnx/test/cpp/function_verify_test.cc |
ONNX_OPERATOR_SET_SCHEMA(
LessOrEqual, 16,
OpSchema()
// ... inputs, outputs, type constraints ...
.TypeAndShapeInferenceFunction(inferenceFunction)
.FunctionBody(R"ONNX(
{
O1 = Less (A, B)
O2 = Equal (A, B)
C = Or (O1, O2)
}
)ONNX"));
With explicit opset version:
.FunctionBody(R"ONNX(...)ONNX", 18) // Valid from opset 18
Referencing attributes with @attr_name:
.FunctionBody(R"ONNX(
{
Alpha = Constant <value_float: float = @alpha>()
AlphaCast = CastLike (Alpha, X)
...
}
)ONNX")
For ops whose decomposition varies based on attributes or optional inputs:
static bool BuildFunctionBodyMyOp(
const FunctionBodyBuildContext& ctx,
const OpSchema& schema,
FunctionProto& functionProto) {
FunctionBuilder builder(functionProto);
// Build graph based on ctx.hasInput(), ctx.getAttribute(), etc.
builder.Add("output = SomeOp (input)");
schema.BuildFunction(functionProto);
return true;
}
// Register:
.SetContextDependentFunctionBodyBuilder(BuildFunctionBodyMyOp)
FunctionBuilder builder(functionProto);
builder.Add("Y = Relu (X)"); // Add node
builder.Const("alpha", std::vector<float>{0.01f}); // Constant tensor
builder.Const1D("axes", int64_t(1)); // 1-D constant
builder.Add(R"( // Multi-line
X_Sub = Sub (X, X_Max)
X_Exp = Exp (X_Sub)
)");
builder.AddOpset("", 18); // Opset dependency
schema.BuildFunction(functionProto); // Finalize
return true;
.SetContextDependentFunctionBodyBuilder(builderForOpset13)
.SetContextDependentFunctionBodyBuilder(builderForOpset18, 18)
Function body strings use the ONNX text format ("onnxtxt"). See the onnxtxt skill for the full syntax cheat sheet, attribute references (@attr_name), Constant <value = ...> forms, CastLike vs Cast, body-subgraph idioms, and parser tests. Quick reminders specific to function bodies:
@attr_name — and only those declared in .Attr(...) calls.CastLike (not Cast) when the target type depends on another input.Define context-dependent function body builders as separate named functions rather than inline lambdas within ONNX_OPERATOR_SET_SCHEMA. The macro expansion makes setting breakpoints on inline lambdas unreliable in debuggers.
Simple string-based .FunctionBody(R"ONNX(...)ONNX") definitions don't have this issue.
python onnx/defs/gen_doc.py
lintrunner -a --output oneline
schema.BuildFunction(functionProto) at end of context-dependent buildersreturn true from the builder functionCast instead of CastLike for dynamic type matching@attr_name for an attribute not declared in .Attr() calls