-
Notifications
You must be signed in to change notification settings - Fork 19
Add initial support for plugins #241
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
base: main
Are you sure you want to change the base?
Conversation
| Issues = "https://github.com/daydreamlive/scope/issues" | ||
|
|
||
| [tool.uv] | ||
| preview = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This silences a warning from uv about extra-build-dependencies usage. I added this because I wanted the CLI output when using commands to be cleaner.
| def plugins(): | ||
| """List all installed plugins.""" | ||
|
|
||
| @suppress_init_output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of this decorator is to suppress logs when using these commands to maintain cleaner CLI output.
| print_version_info() | ||
| sys.exit(0) | ||
| from scope.core.pipelines.registry import ( | ||
| PipelineRegistry, # noqa: F401 - imported for side effects (registry initialization) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PipelineRegistry registers pipelines at import time right now so this is just to avoid unused import errors/warnings while still ensuring that the pipeline registration happens on server startup.
| setTransitionSteps(defaultSteps); | ||
|
|
||
| // Clear prompts if pipeline doesn't support them | ||
| if (pipeline.supportsPrompts === false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of a hack. If the pipeline does not support prompts we just set the prompt to an empty string for now so that the timeline still works, but it doesn't show any text. This felt like the minimal change to get things working without requiring a larger refactor right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about hiding the timeline? I think that the timeline does not bring any value if you don't have prompts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to leave it for now to minimize drastic visual layout changes here and there are other things that having an indication of time progression may be useful for.
| </Tooltip> | ||
| </TooltipProvider> | ||
| )} | ||
| {currentPipeline.modified && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the modified attribute was adding any value so opted to just remove to simplify since we were moving a bunch of attributes to the backend.
leszko
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @yondonfu — this will significantly improve Scope’s extensibility for both us and the community 🚀
Thoughts / suggestions (ordered by priority):
-
Non-GPU support
This currently doesn’t work on non-GPU setups. I added an inline comment with suggested fix(es). -
Separate dependencies per pipeline
We should consider running each pipeline in a separate process (similar to what I started in #173). This would allow each pipeline to have its own dependency set and completely avoid dependency conflicts.
This would be a killer feature - e.g., work like @BuffMcBigHuge’s “PersonaLive!” seems largely about resolving dependency clashes. Isolating pipelines would dramatically speed up adding new ones and aligns with where we want to go anyway. -
Model downloads inside pipelines
Since you already mentioned this: it might be a good time to move model downloading into the pipeline itself. A pipeline plugin without model management feels incomplete and less useful in practice. -
Existing pipelines as plugins
What do you think about migrating our existing pipelines to plugins as well? They could be preinstalled, but this would give us consistent behavior across all pipelines and allow users to uninstall default ones if desired. -
Plugins vs. pipelines
Do we need both concepts? Perhaps we could simplify by having a single concept (e.g., “plugins”), with each plugin containing exactly one pipeline. No strong opinion - just something to consider.
| setTransitionSteps(defaultSteps); | ||
|
|
||
| // Clear prompts if pipeline doesn't support them | ||
| if (pipeline.supportsPrompts === false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about hiding the timeline? I think that the timeline does not bring any value if you don't have prompts.
| """Hook specifications for Scope plugins.""" | ||
|
|
||
| @hookspec | ||
| def register_pipelines(self, register): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to force people to annotate @scope.core.hookimpl in their plugins? Maybe we should just not annotate anything, just when someone installs a plugin, then look into their code and import everything that implements our Pipeline interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is required for using pluggy which relies on the decorator for detecting the hook impl. I'm inclined to just start with that unless we feel like we have a strong reason to start handrolling something ourselves. But, we can always make improvements on this if we need to.
|
A couple additional changes: 7654e86 moves heavy model related imports into init for built-in pipelines. I found this to be the simplest way to avoid triggering those imports when the pipelines are registered - those imports can be a bit slow so better to trigger them as lazily as possible. 6ab629f I noticed a frontend bug where I couldn't change the Input mode after switching to a pipeline. I think the logic flaw was that whenever the input mode changed it would check if the input mode was different than the default and then set the default. I'm not sure when this bug was introduced, but figured would push a quick fix. |
Addressed.
I'm leaning towards starting off with shared venv. A few concerns I have about separate venvs per plugin:
I know that dep conflicts are a challenge. However, we should consider:
I suspect many issues can be addressed with the above. And there will remain some that cannot and the plugin devs will just need to fix their deps. My instinct is that that number is not large enough to justify immediately taking on the complexity of separate venvs per plugin. This doesn't preclude adding that as an option later. [1] I think one of (not the only) reasons that handling deps for plugins is a pain is they are incremental installs (eg using
I'll treat that as something to be addressed next (probably outside of this PR) and want to decide on the shared venv vs separate venv first and test on that.
I'm inclined to address this later and focus on plugin UX/DX first.
I see them as distinct concepts. A plugin can register one or many pipelines and this design leaves room for it introducing other extensions. |
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
…for prompts box Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Rafał Leszko <rafal@livepeer.org> Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Demo
2025-12-16.21-17-56.mp4
Architecture
This is inspired by the llm plugin system.
A plugin is just a Python package that uses the hook system from pluggy that will be installed into the same virtual env as Scope (there is a single virtual env). It can be installed via
uv run daydream-scope installand uninstalled viauv run daydream-scope uninstall. These commands are thin wrappers arounduv pip installanduv pip uninstallallowing us to just rely on typical Python package management and we inherit all the features ofuv pip. We useuv pipinstead ofuv addoruv removebecause the latter would modify the project deps, but we want plugins to be installed into the virtual env without changing the project deps.A plugin adheres to
ScopeHookSpec. At the moment, it has a singlehookspecwhich isregister_pipelines(). A plugin must implement this method and decorate it with@scope.core.hookimpl(example). Theregister_pipelines(0method is responsible for registering one or more pipelines that should be available in Scope.In addition to the
installanduninstallcommands we also introduce:uv run daydream-scope pluginsto list installed pluginsuv run daydream-scope pipelinesto list available pipelinesThese commands are all implemented using click.
The
PipelineRegistryincludes all built-in and plugin pipelines.I've added a simple example of a plugin here that can be installed via:
22d559a also includes frontend changes to:
Missing