-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[STU 192] - RFC - (Reactive) Blocks Developer Experience #982
Comments
Suggestion: Destruct method to blocks to run before flowchart teardown, called when stop signal is recieved. |
Thanks Sasha for this writeup, it's a nice roadmap for things to implement in the next few weeks, I just had a few questions while reading. /**
**NORTH STAR FEATURE**
return the input names and types of the class, by default use reflection to return the In type, but when In is an aribtrary object, allow fully custom outputs.
This is useful for highly generic nodes like ROS pub sub. Beats using a JSON input / output
*/
public default Pair<String, String>[] getFlowchartInputs(); I'm not exactly sure what you mean by "fully custom outputs" when def foo[T](a: T, b: T) -> T:
pass public default Subject ___get_subject___();
/**
a safe user-oriented abstraction of the observable to publish data to the flowchart
*/
protected default void publish(Out out); Here, you expose the reactiveX parts in the class. With the lambda block factory, it makes sense as an easy way to wrap existing blocks, but one thing that was confusing me is that the pseudocode instantiates a FJBlock<In, Out> make_lambda_block(run: (input: In, hooks: Function[]) => Out) {
// we pass in class method "memoize" as a hook to the lambda because it lets us have some more built in functions (hooks) to manage the block without class access
return new FJBlock<In, Out>() {
//...the rest of the implenentation of abstract methods
on_next(input) {
this.publish(run(input, [this.memoize]));
}
}
} I think in the actual implementation we would just have a class LambdaBlock(FJBlock):
def __init__(self, block_func):
self.func = block_func
...
def on_next(*args, **kwargs):
return self.func(*args, **kwargs):
# other default implementations Also, since you're injecting the hooks through the function parameters, this means when migrating all of the existing function blocks to this format, we would have to add an optional |
For this, I was thinking about fully runtime defined I/O. For example, a web API node whose output is an object with the fields of what's queried from the API, or a hardware node which outputs differently based on what's connected. I put it as a "north star" feature because it may be difficult to implement or narrowly useful.
Yes we may have to make changes to out to enable this API, I think the internal reactive structure of the node will need a rework, but that implementational detail may be something we can iron out after establishing the design from a developer interface perspective.
This is an anonymous class, some relatively arcane Java syntax. My bad for using something that isn't all too common. It's a shorthand for directly instantiating an abstract class by filling in any abstract methods on instantiation. In effect it is the Java equivalent of a method taking a function as input.
If that's a better way to handle this idea in Python, I think that's fair. There isn't a technical reason it should be a factory instead of a constructor.
I am imagining that the function blocks and hooks are going to be used primarily for short script or transformation nodes written in an in-studio editor. I wouldn't want someone jumping into Flojoy to have to learn the shape of the FJBlock class unless they need to, which they don't if they're making a simple custom block that does some math or something. Do you think there's a better way to approach that use case? My goal with the function block was dual:
These two goals can also be split off into 2 different implementations if that is deemed to be optimal. |
Hi!
Could you elaborate on the plan for handling blocks with special dependencies? Would this be the same as it was previously? @flojoy(deps={"torch": "2.0.1", "torchvision": "0.15.2"})
def DEEPLAB_V3(default: Image) -> Image:
... Would all dependencies be handled with poetry? |
I don't see any reason we can't keep using the old dependency management system. I would say it will stay completely the same. Maybe the annotation above the class instead of the function. @itsjoeoui Thoughts? |
Hi @TheBigSasha, thank you for writing this comprehensive RFC. It seems the "Publish" subsection misses a sentence at its end. IIRC, poetry was used because conda-lock has some slowness or because conda-forge does not ship all the packages (e.g. for instance the headless version of opencv which is needed since OpenCV's license is tight to Qt's). Let's wait for @itsjoeoui's answer? Apart from that, |
Thanks for the note, I updated the publish section with an example :) |
Mirror of STU-192 on Linear
Question: How do we make it easy and powerful to write flojoy nodes?
Subquestion: What is the purpose of a flojoy node?
Subquestion: What control vs abstraction trade offs do we want to make?
Principles:
Design Proposal
Function Blocks, Hooks
A block can contain hooks for effects/memoization, and internal state which abstract away the RX subjects.
Publish
The publish or "state" hook works just like ReactJS useState — upon calls to publish (setState), changes are propagated. Otherwise, changes to state do not propagate in the reactive compute graph. This can be used for hardware support, like so:
Instead of returning nulls, this function block filters out evens and propagates only odds by using the publish hook.
Memoize / Effect
The memoize hook is particularly useful to ML or other long compute tasks. It takes a lambda and a list of parameter keys, and it runs the lambda once, and then afterwards only when one of the parameter keys changes. Think useMemo and useEffect in React terms. There is no distinction here, since React distinguishes these based on time of run, primarily for DOM access, which is not an issue for us.
The text was updated successfully, but these errors were encountered: