Services & Extensions
Understanding dependency injection, service providers, and modular extensions.
Daydreams uses a combination of Dependency Injection (DI), Service Providers, and an Extension system to manage dependencies, initialize components, and organize functionality in a modular way.
Dependency Injection (container.ts
)
At the heart of the framework's modularity is a simple Dependency Injection
container, created using createContainer()
. The container is responsible for
instantiating and providing access to various services and components throughout
the agent's lifecycle.
Purpose:
- Decouples components by removing the need for them to know how to create their dependencies.
- Manages the lifecycle of services (e.g., ensuring only one instance of a database client exists).
- Makes components like loggers, clients, or configuration easily accessible.
Core Methods:
container.register(token, factory)
: Registers a factory function. A new instance is created every timeresolve
is called for thetoken
.container.singleton(token, factory)
: Registers a factory function, but the instance is created only once on the firstresolve
call. Subsequent calls return the same instance.container.instance(token, value)
: Registers a pre-existing object instance directly.container.resolve<Type>(token)
: Retrieves the instance associated with thetoken
. Throws an error if the token is not registered.container.alias(aliasToken, originalToken)
: Creates an alternative name (aliasToken
) to resolve an existingoriginalToken
.
The main Agent
instance, Logger
, TaskRunner
, and other core components are
typically registered within the container when createDreams
is called.
Service Providers (serviceProvider.ts
)
While you could register everything directly with the container, Daydreams uses a Service Provider pattern to organize the registration and initialization (booting) of related services, especially within extensions.
Purpose:
- Groups related service registrations.
- Provides a dedicated
boot
phase for initialization logic that might depend on other services already being registered (e.g., connecting a client after its configuration is registered).
Defining a Service Provider:
Use the service
helper function:
Lifecycle:
- Registration: When a service provider is added to the agent (usually via
an extension), its
register
method is called immediately by theServiceManager
(created internally increateDreams
). - Booting: When
agent.start()
is called, theServiceManager
iterates through all registered service providers and calls theirboot
methods after allregister
methods have completed.
Extensions (extension()
helper)
Extensions are the primary mechanism for packaging and distributing reusable Daydreams functionality. They bundle together contexts, actions, inputs, outputs, and service providers.
Purpose:
- Encapsulate features (e.g., Discord integration, ChromaDB support).
- Simplify agent configuration by adding features with a single entry.
- Promote code reuse.
Defining an Extension:
Use the extension
helper function:
Usage and Lifecycle:
-
Configuration: Pass extensions to
createDreams
in theextensions
array: -
Merging: When
createDreams
initializes, it merges all components (contexts
,actions
,inputs
,outputs
,events
) defined within the extensions into the agent's main registry. -
Service Registration: It registers all
services
defined in the extensions with theServiceManager
. -
Installation & Booting: When
agent.start()
is called:- The
install
method of each extension is executed (if defined). - The
ServiceManager
boots all registered services (calling theirboot
methods).
- The
Extensions provide a powerful way to structure agent capabilities, making it easy to combine built-in features (like CLI, Discord, Chroma) with custom logic.