-
Notifications
You must be signed in to change notification settings - Fork 3
refactor(plugin-local-ai): migrate build system to Bun and modularize architecture #13
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
Open
odilitime
wants to merge
1
commit into
1.x
Choose a base branch
from
odi-dev
base: 1.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| # Changelog | ||
|
|
||
| All notable changes to `@elizaos/plugin-local-ai` are documented in this file. | ||
| Format based on [Keep a Changelog](https://keepachangelog.com/). Newest entries first. | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
|
|
||
| - **Startup configuration banner** (`src/banner.ts`) | ||
| - Displays compact table showing models directory, cache directory, loaded model filenames, and embedding dimensions | ||
| - Shows status indicators: `(default)` for default values, `(set)` for user-configured values | ||
| - **WHY**: Local AI involves multiple file paths and configurations. The banner makes it immediately obvious which models are loaded and where they're stored, critical for debugging "model not found" errors. | ||
|
|
||
| - **Modular architecture** (`src/models/`, `src/utils/`, `src/constants.ts`, `src/manager.ts`) | ||
| - Extracted `LocalAIManager` from `index.ts` to dedicated `manager.ts` (core logic) | ||
| - Created `constants.ts` for shared defaults (`wordsToPunish`, default model filenames) | ||
| - Created `utils/config.ts` with helper functions: `getSetting`, `getSmallModel`, `getLargeModel`, `getEmbeddingModel` | ||
| - Created `models/` directory with separate files: `text.ts`, `embedding.ts`, `image.ts`, `audio.ts`, `tokenizer.ts` | ||
| - **WHY**: Original `index.ts` was 2000+ lines mixing model handlers, initialization, config resolution, and manager logic. Smaller focused modules are easier to test, debug, and maintain. Follows pattern from `@elizaos/plugin-openai`. | ||
|
|
||
| - **Runtime-aware configuration resolution** (`src/utils/config.ts`, model handlers) | ||
| - `getSetting(runtime, key, defaultValue)` checks `runtime.getSetting()` first, then falls back to `process.env` | ||
| - `LocalAIManager.initializeEnvironment(runtime?)` accepts optional `IAgentRuntime` parameter | ||
| - All model handlers pass `runtime` to manager initialization | ||
| - **WHY**: Plugin must respect both character-level settings (via `runtime.getSetting`) and environment variables. Original code only checked `process.env`, completely ignoring character configuration. | ||
|
|
||
| - **Consistent logging prefixes** (all modules) | ||
| - Added `[LocalAI]` prefix to all logger calls | ||
| - Removed emoji from log messages | ||
| - **WHY**: Makes logs greppable (`grep '\[LocalAI\]' logs.txt`) and clearly identifies which plugin generated each message. Emoji breaks certain terminal configurations and log parsers. | ||
|
|
||
| ### Changed | ||
|
|
||
| - **Build system: tsup → Bun.build** (`build.ts`, `package.json`) | ||
| - Replaced `tsup.config.ts` with custom `build.ts` using `@elizaos/build-utils` | ||
| - Uses monorepo's shared `createBuildRunner` for consistent configuration | ||
| - Generates Node ESM, browser bundle, and CJS outputs | ||
| - Declaration files via `tsc --emitDeclarationOnly` | ||
| - Moved `tsup` from `dependencies` to `devDependencies` | ||
| - **WHY**: Standardizing build configuration across all plugins reduces maintenance burden. `tsup` had issues with native dependency bundling; Bun's builder handles them correctly and is significantly faster. | ||
|
|
||
| - **Test framework: vitest → bun:test** (`__tests__/*.test.ts`, `__tests__/test-utils.ts`) | ||
| - Converted all test files from `vitest` to Bun's native test runner | ||
| - Replaced `vi.fn()` with `mock()`, `describe`/`expect`/`test` now from `bun:test` | ||
| - Added `mock.module()` calls at top of each test file to mock native dependencies BEFORE plugin imports them | ||
| - Mocked dependencies: `node-llama-cpp`, `@huggingface/transformers`, `nodejs-whisper`, `node:child_process` | ||
| - **WHY**: `node-llama-cpp` has incompatible import patterns with vitest's module mocking, causing `SyntaxError: Missing 'default' export in module 'node:fs'`. The issue was that static imports of native dependencies happened before test setup, and vitest couldn't intercept them. Bun's `mock.module()` hoists mocks before imports execute, preventing load-time crashes. Additionally, Bun's test runner is 3-5x faster. | ||
|
|
||
| ### Technical Details | ||
|
|
||
| #### Module-level mocking pattern (critical for native dependencies) | ||
|
|
||
| The plugin uses heavy native dependencies (`node-llama-cpp` for GGUF models, `@huggingface/transformers` for vision/TTS, `nodejs-whisper` for transcription) that load WASM/native bindings at import time. In test environments, these bindings fail because models aren't present. | ||
|
|
||
| **Problem**: Static imports execute before test setup code: | ||
| ```typescript | ||
| // ❌ This fails — node-llama-cpp loads native code before mock() runs | ||
| import { localAiPlugin } from '../src/index'; | ||
| mock.module('node-llama-cpp', () => ({ ... })); | ||
| ``` | ||
|
|
||
| **Solution**: Hoist mocks using `mock.module()` at the TOP of test files: | ||
| ```typescript | ||
| import { mock, describe, expect, test } from 'bun:test'; | ||
|
|
||
| // ✅ Mock BEFORE any plugin imports | ||
| mock.module('node-llama-cpp', () => ({ | ||
| getLlama: mock(async () => ({ /* mock implementation */ })), | ||
| LlamaChatSession: class {}, | ||
| // ... other exports | ||
| })); | ||
|
|
||
| // NOW safe to import plugin (it sees the mocked module) | ||
| import { localAiPlugin } from '../src/index'; | ||
| ``` | ||
|
|
||
| Bun's `mock.module()` is hoisted like `jest.mock()` — it executes before any imports in the file, regardless of source order. | ||
|
|
||
| #### Configuration resolution flow | ||
|
|
||
| 1. Model handlers call manager: `await localAIManager.initializeEnvironment(runtime)` | ||
| 2. Manager calls config helpers: `const smallModel = getSetting(runtime, 'LOCAL_SMALL_MODEL')` | ||
| 3. Config helper checks runtime first: `runtime.getSetting(key)` (character settings) | ||
| 4. Falls back to environment: `process.env[key]` | ||
| 5. Returns default if neither exist: `defaultValue` | ||
|
|
||
| This ensures character-level configuration overrides environment variables, which override plugin defaults. | ||
|
|
||
| ## [1.6.6-alpha.5] - 2026-01-XX | ||
|
|
||
| ### Changed | ||
|
|
||
| - **Logger API compatibility**: Updated all logging to use string-only format (no object arguments) | ||
| - **Model handler signatures**: Aligned with `@elizaos/core` union types (`string | ParamsType | null`) | ||
| - **GenerateText refactor**: Separated `modelType` from `GenerateTextParams` (passed as explicit parameter) | ||
| - **node-llama-cpp upgrade**: Updated from v3.10.0 to v3.15.1 for better GGUF support | ||
|
|
||
| ### Fixed | ||
|
|
||
| - All handlers now properly handle `null` and `string` parameter types in addition to typed objects | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -99,8 +99,151 @@ const audioStream = await runtime.useModel(ModelType.TEXT_TO_SPEECH, 'Text to co | |||||||
| const transcription = await runtime.useModel(ModelType.TRANSCRIPTION, audioBuffer); | ||||||||
| ``` | ||||||||
|
|
||||||||
| ## Architecture | ||||||||
|
|
||||||||
| The plugin uses a modular architecture with clear separation of concerns: | ||||||||
|
|
||||||||
| ``` | ||||||||
| src/ | ||||||||
| index.ts Plugin entry — registers model handlers | ||||||||
| manager.ts LocalAIManager singleton — coordinates all model operations | ||||||||
| banner.ts Startup configuration display | ||||||||
| constants.ts Shared constants and defaults | ||||||||
| models/ | ||||||||
| text.ts TEXT_SMALL, TEXT_LARGE handlers | ||||||||
| embedding.ts TEXT_EMBEDDING handler | ||||||||
| image.ts IMAGE_DESCRIPTION handler | ||||||||
| audio.ts TEXT_TO_SPEECH, TRANSCRIPTION handlers | ||||||||
|
||||||||
| audio.ts TEXT_TO_SPEECH, TRANSCRIPTION handlers | |
| audio.ts TEXT_TO_SPEECH, TRANSCRIPTION handlers | |
| object.ts OBJECT_SMALL, OBJECT_LARGE handlers |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Placeholder date
2026-01-XXshould be filled in.The version entry has an incomplete date. Replace with the actual release date before merging.
🤖 Prompt for AI Agents