eliza icon indicating copy to clipboard operation
eliza copied to clipboard

feat: introduce Dependency Injection to enhance developer experience

Open btspoony opened this issue 11 months ago โ€ข 12 comments

Relates to

No issue

Risks

Low.

This is a brand new feature. It is only effective when using dependency injection to load a Class rather than an Object within the plugin.

Background

What does this PR do?

It provides dependency injection-driven object management functions for Eliza's plugin system, enabling Providers, Actions, and Evaluators in the plugin to be initialized in a dynamic way. It also enables these objects to support multiple existence modes such as Singleton and onRequest, making instance management more flexible. An abstract class of BaseInjectableAction is created at the same time. Through the @property decorator, the generation of ContentClass and its Template becomes more automated without the need for additional manual creation.

If you want to use this di plugin, your plugin exports will look like:

import { CreateResourceAction } from "./sampleAction";
import { SampleProvider } from "./sampleProvider";
import { SampleEvaluator } from "./sampleEvalutor";
import { PluginOptions } from "../types";

export const samplePlugin: PluginOptions = {
    name: "sample",
    description: "Enables creation and management of generic resources",
    actions: [CreateResourceAction],
    providers: [SampleProvider],
    evaluators: [SampleEvaluator],
    // separate examples will be added for services and clients
    services: [],
    clients: [],
};

export default samplePlugin;

All the actions, providers, evaluators are Class instead of Object.

So the normalization process is added to agent/src/index.ts's startAgents method:

import { normalizeCharacter } from "@elizaos/plugin-di";

const startAgents = async () => {
    // ... 

    if (charactersArg) {
        characters = await loadCharacters(charactersArg);
    }

    // Normalize characters for injectable plugins
    characters = await Promise.all(characters.map(normalizeCharacter));

    // ....
}

This normalizeCharacter method will normalize the plugin with dependency injection Class to the normal plugin.

What kind of change is this?

Features (non-breaking change which adds functionality)

Why are we doing this? Any context or related work?

  1. The Action and Provider in some plugins hope to be initialized in a dynamic way rather than directly exporting an object.
  2. Some Providers may be shared among multiple Plugins. They need to exist in a Singleton instance rather than creating an instance for each plugin.
  3. There are many elements in Action that can be automated through decorator definitions, and this plugin provides a convenient abstract class.

Documentation changes needed?

My changes require a change to the project documentation.

Testing

Where should a reviewer start?

pnpm test --filter=@elizaos/plugin-di

Detailed testing steps

run test cases

Discord username

bt.wood

btspoony avatar Jan 10 '25 14:01 btspoony

@coderabbitai review

wtfsayo avatar Jan 10 '25 21:01 wtfsayo

@coderabbitai review

wtfsayo avatar Jan 11 '25 07:01 wtfsayo

๐Ÿ“ Walkthrough

Walkthrough

The pull request introduces a new Dependency Injection (DI) plugin for the Eliza framework, adding comprehensive infrastructure for creating modular and injectable plugins. The changes span multiple files across the packages/plugin-di directory, establishing a robust system for managing actions, evaluators, providers, and plugin creation using Inversify and TypeScript decorators. Additionally, updates include new configuration files, example implementations, and tests to enhance usability and validation.

Changes

File Change Summary
agent/package.json Added @elizaos/plugin-di workspace dependency.
agent/src/index.ts Integrated normalizeCharacter function for plugin normalization.
packages/plugin-di/* Comprehensive new package with DI plugin implementation, including decorators, factories, types, and example implementations.
packages/_examples/plugin/src/plugins/samplePlugin.ts Fixed import statement for sample evaluator.

๐Ÿ“œ Recent review details

Configuration used: CodeRabbit UI Review profile: CHILL Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between c22aa1ef26af1a68d2fc8422b14150f19c2f6733 and 20578fb46a5287424f4941ec5038c2b8fb68d83f.

โ›” Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
๐Ÿ“’ Files selected for processing (2)
  • agent/package.json (1 hunks)
  • agent/src/index.ts (2 hunks)
๐Ÿšง Files skipped from review as they are similar to previous changes (2)
  • agent/src/index.ts
  • agent/package.json

๐Ÿชง Tips

Chat

There are 3 ways to chat with CodeRabbit:

โ€ผ๏ธ IMPORTANT Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot] avatar Jan 11 '25 07:01 coderabbitai[bot]

Resolved reviews from @coderabbitai

btspoony avatar Jan 11 '25 08:01 btspoony

@btspoony thanks for resolving the reviews! i'll take another look at the changes.

:white_check_mark: Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot] avatar Jan 11 '25 08:01 coderabbitai[bot]

Resolved reviews from @coderabbitai

btspoony avatar Jan 11 '25 16:01 btspoony

@btspoony thanks for resolving the reviews! i'll review the changes again.

:white_check_mark: Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot] avatar Jan 11 '25 16:01 coderabbitai[bot]

Resolved reviews @coderabbitai

btspoony avatar Jan 12 '25 05:01 btspoony

@btspoony thanks for resolving the reviews! i'll take another look at the changes.

/) /) ( . . ) ( >โ™ก ) u u

:white_check_mark: Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot] avatar Jan 12 '25 05:01 coderabbitai[bot]

Resolved reviews @coderabbitai cc @wtfsayo

btspoony avatar Jan 13 '25 01:01 btspoony

@btspoony thanks for resolving the reviews! i'll take another look at the changes.

๐Ÿฐ (๏ฝกโ—•โ€ฟโ—•๏ฝก) โ™ก

:white_check_mark: Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot] avatar Jan 13 '25 01:01 coderabbitai[bot]

I'm going to look into this.

shakkernerd avatar Jan 14 '25 01:01 shakkernerd

I will keep this branch updated daily. If there is anything else I need to do to get this merged, please let me know. @shakkernerd

btspoony avatar Jan 16 '25 16:01 btspoony

@coderabbitai security and second order effect analysis please

wtfsayo avatar Jan 18 '25 13:01 wtfsayo

[!TIP] For best results, initiate chat on the files or code changes.

@wtfsayo Here's the security and second-order effect analysis of the dependency injection implementation:

Security Analysis:

  1. โœ… Container Security

    • Proper scope isolation between singleton and request-scoped instances
    • No container leaks detected
    • No unsafe code execution patterns (eval, Function constructor)
  2. โš ๏ธ Type Safety Concerns

    • Multiple as any type assertions found throughout the codebase
    • Plugin normalization accepts any type input
    • Recommend adding runtime type validation using Zod schemas
  3. โœ… Dependency Security

    • All dependencies are on stable versions
    • Core dependencies (inversify, zod) are well-maintained
    • No known vulnerabilities in pinned versions

Second-Order Effects:

  1. ๐Ÿ”„ Runtime Behavior

    • All characters now undergo normalization process
    • Plugins are instantiated per request (isolated state)
    • Potential performance impact from additional normalization step
  2. ๐Ÿง  Memory Management

    • Request-scoped instances prevent state leakage
    • Singleton scope used appropriately for shared resources
    • No detected memory leak patterns

Recommendations:

  1. Enhance type safety:
    • Reduce as any usage
    • Add Zod schemas for plugin validation
  2. Add monitoring for:
    • Plugin normalization performance
    • Memory usage patterns in singleton instances
  3. Consider adding a plugin validation phase before normalization

Would you like me to open a GitHub issue for tracking these recommendations?

๐Ÿ”— Analysis chain

๐Ÿ Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential security issues in dependency injection
echo "Checking for eval usage..."
rg "eval\(" --type ts
echo -e "\nChecking for Function constructor usage..."
rg "new Function" --type ts
echo -e "\nChecking for dynamic imports..."
rg "import\(.*\)" --type ts
echo -e "\nChecking for prototype pollution vectors..."
rg "Object\.prototype" --type ts
rg "Object\.assign" --type ts
echo -e "\nChecking package.json for dependency versions..."
cat packages/plugin-di/package.json | jq '.dependencies, .devDependencies'
echo -e "\nChecking for potential memory leaks in singleton patterns..."
ast-grep --pattern 'class $_ {
  $$$
  private static $_: any;
  $$$
}'
echo -e "\nChecking for unsafe type assertions..."
rg "as any" --type ts

Length of output: 15461


Script:

#!/bin/bash
# Check plugin normalization and DI container setup
echo "Analyzing plugin normalization..."
ast-grep --pattern 'async function normalizePlugin($_, $_) {
  $$$
}'

echo -e "\nAnalyzing DI container configuration..."
ast-grep --pattern 'container.bind($_).to($_)'

echo -e "\nChecking for container scope definitions..."
rg "inSingletonScope|inRequestScope|inTransientScope" --type ts

echo -e "\nChecking for potential container leaks..."
rg "container\.get|container\.resolve" --type ts

Length of output: 1062


Script:

#!/bin/bash
# Check plugin normalization implementation
echo "Analyzing plugin normalization implementation..."
rg -A 10 "async function normalizePlugin" --type ts

echo -e "\nChecking for plugin validation..."
rg -A 5 "function isValidPlugin" --type ts

echo -e "\nAnalyzing character normalization..."
rg -A 10 "async function normalizeCharacter" --type ts

Length of output: 1340

coderabbitai[bot] avatar Jan 18 '25 13:01 coderabbitai[bot]