RFC: Per-object node parameters (object-level params alongside node-level config) #1292
stepmikhaylov
started this conversation in
Ideas
Replies: 1 comment
-
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
Today a node's parameters are resolved once at startup and reused for every object the node processes. This proposal adds object-level parameters: a parameter can be declared with
Objectscope and then set per object — by the external client when it submits the object, or by an upstream node at runtime — while the consuming node reads it through a single pre-merged accessor,self.params, on the node instance.A node only ever sees its own parameters; reaching another node's parameters is a deliberate, addressed step.
Problem
Parameters flow author → schema → values → runtime in four hops:
nodes/src/nodes/<name>/services.json: a flatfieldsdict (the param vocabulary) plus ashapearray of{section, title, properties}groups. A field becomes a "subsection" when it carriessection/object(services.cpp:448)."Pipe"is just one section name alongsideSource/Target/Transform.services.cppemits, per section,schema[section].schema+schema[section].ui(RJSF schema + uiSchema;processSubsectionservices.cpp:634)./services(services.py) is a pure pass-through of this C++-generated schema.@rjsf/core; saved values land inPipelineComponent.config(pipeline.ts:68). Noteconfig.parametersexists on every node today but is always{}— unused.IGlobal.beginGlobal()resolves config viaConfig.getNodeConfig()=profile defaults ◂ connConfig overrides(config.py:70-190). That one resolved dict is reused for every object. Per object,open(Entry)/writeX()fire on anEntry(entry.hpp) carrying per-object JSON bags (metadata,objectTags,response).There is no layer between the resolved config and the object. That is the gap: you cannot vary a node's parameter per object without redeploying a differently-configured node.
Decisions
Objectsection inshape. It references field names from the sharedfieldsdict (no re-declaration). A field listed in bothPipeandObject⇒ node value is the default, per-object value overrides.Entrycarries a node-id-namespaced bag,entry.parameters[nodeId]; each node reads only its own key. v1 fills it by explicit addressing (client submit kwarg, orentry.setParametersFor(nodeId, …)from an upstream node). The consumer always readsself.params, so a future lane-wiring delivery model can be layered in without touching node code.self.paramsis the merged view for the current object, stored on the node instance alongside itscurrentObjectreference (refreshed per object). It is not stored on the sharedEntry(see Concurrency).Objectschema bucket — no new schema engine.currentObject.parameters[selfId] ◂ node config ◂ profile defaults.How it looks once shipped (client coding POV)
1. Service configuration — node- and object-level parameters
2. The Python object contract (transport vs. resolved view)
3. Client Python code that sets the object parameter at runtime
4. Node Python code that uses the object parameter at runtime
In-pipeline producer variant (an upstream node computes a param for a downstream node):
Concurrency: why the resolved view is instance-local
The
Entryis opened and closed to all nodes in the pipeline at the same time — it is a single shared object, processed concurrently. Therefore:Entry(every node would see the same mutated copy). It lives on the per-node instance (self.params), alongsidecurrentObject, refreshed per object.Entrycarries only the namespaced transport bag; isolation comes from each node reading onlyparameters[selfId].setParametersFor) needs a defined visibility rule: the producer must populatecurrentObject.parameters["C"]before consumer C resolvesself.params. With concurrent open there is no implicit upstream→downstream ordering, so the proposal is to resolveself.paramslazily on first access (rather than eagerly at object-bind time). The client-submit path is unaffected — params are present on theEntrybefore it enters the pipeline.Implementation phases & touch-list
Phase 1 —
Objectsection: convention + schema generationPhase 2 — Carry per-object params on submit (protocol)
// packages/client-typescript/src/client/... — mirror the `parameters` arg on the TS pipe/send.Phase 3 — Entry transport bag + instance-local resolution (C++)
Phase 4 — Python merge + accessor
Phase 5 — UI: render the object form
Phase 6 — Docs (co-located rule) + verify
Recommended first PR — thin vertical slice
feat/RR-<ticket>-object-parametersoffdevelop: Phases 1–4 on one node (temperaturein anObjectsection), clientsend(..., parameters={"llm_1": {…}}), node readsself.params['temperature']. Proves submit → Entry transport → instance-local merge →self.paramsend-to-end before Phase 5 UI and rollout.Open questions for discussion
Objectsection +self.params+setParametersForvs. alternatives (Runtime/PerObject,self.objectParams).Objectfield schema at resolution time, or trust the producer?Entryis open to all nodes at once, a producer'ssetParametersFormust land before the consumer readsself.params. Proposal: lazy resolution ofself.params. Sufficient, or do we need an explicit ordering/barrier guarantee?secure+Object— proposal forbids per-object secrets through the data plane. Agreed?Drafted with Claude Code.
Beta Was this translation helpful? Give feedback.
All reactions