With the basic plugin system now landed (ffaf2b6), and @Piktrip's excellent work on OpenRouter integration (#23) as a starting point, it's time to make it possible for plugins to provide backends that Waidrin can use for inference. The main challenges are being generic enough to serve all possible plugins of this type, and maintaining a clean engine/frontend separation.
Here's my current plan to achieve this:
The engine interface
Waidrin's engine relies on just two functions to communicate with the LLM: getResponse, and getResponseAsObject. The logical first step would be to extract those function's signatures (without the params, as those aren't generic) into a Backend TypeScript interface. The plugin would then provide an implementation of this interface, or, better, any number of them, as a plugin might want to provide multiple backends:
type Plugin = {
...
getBackends(): Promise<Record<string, Backend>>;
};
The response is a map from the backend name to the implementation, and can contain arbitrarily many entries. When the plugin is loaded, the loader checks whether it implements getBackends, and, if it does, calls it and adds the resulting backend entries to a Record<string, Backend> map in the global state. The state also contains an activeBackend: string field that decides which of the backends is used by the engine.
The frontend
The Plugin interface is frontend-agnostic. Any frontend-specific logic is handled by the opaque context parameter passed to init. The context provides environment-specific functionality to the plugin, and for Waidrin's React frontend, will contain various functions that allow the plugin to interact with it.
My idea is to add something like the following function to the context:
addBackendConfigurationPage(backendName: string, displayName: string, pageComponent: () => React.ReactNode): void
When the plugin calls that function, Waidrin adds a new tab to the connection screen, titled displayName, with pageComponent as its contents. The plugin is free to do whatever it wants in this component, and can connect it to the Backend implementation it provides in whatever way it wants. When the user selects this tab, Waidrin sets activeBackend to backendName, and everything should magically work. This way, plugins can provide any number of backend implementations, and separate configuration pages for each of them, while activation is completely controlled by the application. Waidrin's plugin system already has a rudimentary mechanism for plugin-specific settings, which can be used and extended as needed to serve backend plugins.
Backend provider plugins will be a critical feature making Waidrin usable by more people, and it's very important to get them right. If you have any comments on this proposal, now is the time!
With the basic plugin system now landed (ffaf2b6), and @Piktrip's excellent work on OpenRouter integration (#23) as a starting point, it's time to make it possible for plugins to provide backends that Waidrin can use for inference. The main challenges are being generic enough to serve all possible plugins of this type, and maintaining a clean engine/frontend separation.
Here's my current plan to achieve this:
The engine interface
Waidrin's engine relies on just two functions to communicate with the LLM:
getResponse, andgetResponseAsObject. The logical first step would be to extract those function's signatures (without theparams, as those aren't generic) into aBackendTypeScript interface. The plugin would then provide an implementation of this interface, or, better, any number of them, as a plugin might want to provide multiple backends:The response is a map from the backend name to the implementation, and can contain arbitrarily many entries. When the plugin is loaded, the loader checks whether it implements
getBackends, and, if it does, calls it and adds the resulting backend entries to aRecord<string, Backend>map in the global state. The state also contains anactiveBackend: stringfield that decides which of the backends is used by the engine.The frontend
The
Plugininterface is frontend-agnostic. Any frontend-specific logic is handled by the opaquecontextparameter passed toinit. The context provides environment-specific functionality to the plugin, and for Waidrin's React frontend, will contain various functions that allow the plugin to interact with it.My idea is to add something like the following function to the context:
When the plugin calls that function, Waidrin adds a new tab to the connection screen, titled
displayName, withpageComponentas its contents. The plugin is free to do whatever it wants in this component, and can connect it to theBackendimplementation it provides in whatever way it wants. When the user selects this tab, Waidrin setsactiveBackendtobackendName, and everything should magically work. This way, plugins can provide any number of backend implementations, and separate configuration pages for each of them, while activation is completely controlled by the application. Waidrin's plugin system already has a rudimentary mechanism for plugin-specific settings, which can be used and extended as needed to serve backend plugins.Backend provider plugins will be a critical feature making Waidrin usable by more people, and it's very important to get them right. If you have any comments on this proposal, now is the time!