Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
# Dependencies
node_modules
.turbo

# Build outputs
dist
build
*.tsbuildinfo
.turbo
.turbo-tsconfig.json

# Environment files
.env
.eliza**
.env.local
.env.production
.env.bak

# IDE
.idea
.vscode
.zed
.DS_Store

# Test coverage
coverage
.nyc_output

# Logs
*.log
logs

# Cache
cache
.cache
tokencache

# Temporary files
*.tmp
*.temp
.tmp

# Bundler artifacts
tsup.config.bundled_*.mjs
# prr state file (auto-generated)
.pr-resolver-state.json
16 changes: 16 additions & 0 deletions .prr/lessons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# PRR Lessons Learned

> This file is auto-generated by [prr](https://github.com/elizaOS/prr).
> It contains lessons learned from PR review fixes to help improve future fix attempts.
> You can edit this file manually or let prr update it.
> To share lessons across your team, commit this file to your repo.

## Global Lessons

- Fixer attempted disallowed file(s): __tests__/index.test.ts. Only edit the file(s) listed in TARGET FILE(S): src/index.ts, src/index.test.ts.

## File-Specific Lessons

### src/index.ts

- Fix for src/index.ts:208 - Need to add unit tests in test/ directory that verify textStream/textStreamDone event ordering and timeout behavior using mocked models
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,24 @@ const object = await runtime.useModel(ModelType.OBJECT_LARGE, {
- You can pre-download models using `ollama pull modelname`
- Check model availability with `ollama list`

## Publishing (standalone repo)

When developing in a monorepo you keep `@elizaos/core` as `workspace:*`. When publishing from the standalone repo (e.g. after syncing from monorepo), the published package must use a real version so consumers can install.

- **After syncing from monorepo:** run `bun run prepare-publish` to set `@elizaos/core` to the version in `publishCoreVersion` (or set `CORE_VERSION` env). Commit that change so installs work in the standalone repo.
- **Before every publish:** `prepublishOnly` runs automatically and ensures the packed tarball has the correct core version even if `package.json` still has `workspace:*`.
- To change the published core version, update `publishCoreVersion` in `package.json` (or use `CORE_VERSION=1.8.0 bun run prepare-publish`).

### Git hooks (monorepo)

To always **commit** the verified core version and **check out** with `workspace:*` for local dev, install the hooks from the plugin directory (in the monorepo):

```bash
cd packages/plugin-ollama && bun run install-git-hooks
```

- **pre-commit:** Sets `@elizaos/core` to `publishCoreVersion` and stages `package.json`, so every commit has the publishable version.
- **post-checkout:** In a monorepo only, sets `@elizaos/core` back to `workspace:*` after checkout so installs use the local workspace. Not run when this repo is the standalone plugin repo (so installs keep working there).

## License

Expand Down
47 changes: 47 additions & 0 deletions build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bun
import { $ } from "bun";

async function build() {
const totalStart = Date.now();
const pkg = await Bun.file("package.json").json();
const externalDeps = [
...Object.keys(pkg.dependencies ?? {}),
...Object.keys(pkg.peerDependencies ?? {}),
];

// Use the clean script from package.json
if (pkg.scripts?.clean) {
console.log("🧹 Cleaning...");
// Note: always runs clean to ensure a fresh build environment without stale artifacts
await $`bun run clean`.quiet();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build script deletes node_modules before tsc needs them

High Severity

build.ts unconditionally runs bun run clean before building. The clean script includes rm -rf node_modules, which deletes all installed dependencies. The subsequent tsc --project tsconfig.build.json step (line 38) requires node_modules to resolve type declarations for imports like @elizaos/core and ai. This causes tsc to fail or produce broken .d.ts files with unresolved types.

Additional Locations (1)
Fix in Cursor Fix in Web

}

const esmStart = Date.now();
console.log("🔨 Building @elizaos/plugin-ollama...");
const esmResult = await Bun.build({
entrypoints: ["src/index.ts"],
outdir: "dist",
target: "node",
format: "esm",
sourcemap: "external",
minify: false,
external: externalDeps,
});
if (!esmResult.success) {
console.error(esmResult.logs);
throw new Error("ESM build failed");
}
console.log(`✅ Build complete in ${((Date.now() - esmStart) / 1000).toFixed(2)}s`);

const dtsStart = Date.now();
console.log("📝 Generating TypeScript declarations...");
await $`tsc --project tsconfig.build.json`;
console.log(`✅ Declarations generated in ${((Date.now() - dtsStart) / 1000).toFixed(2)}s`);

console.log(`🎉 All builds finished in ${((Date.now() - totalStart) / 1000).toFixed(2)}s`);
}

build().catch((err) => {
console.error("Build failed:", err);
process.exit(1);
});
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,27 @@
],
"dependencies": {
"@ai-sdk/ui-utils": "^1.2.8",
"@elizaos/core": "^1.0.0",
"@elizaos/core": "1.7.2",
"ai": "^4.3.9",
"js-tiktoken": "^1.0.18",
"ollama-ai-provider": "^1.2.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsup listed as a runtime dependency

tsup is a build tool and should be in devDependencies, not dependencies. Keeping it in dependencies causes it to be installed in production environments and inflates the package's install footprint for consumers.

Suggested change
"ollama-ai-provider": "^1.2.0",
"tsup": "8.4.0"

Move the line to devDependencies.

"tsup": "8.4.0"
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"build": "bun run build.ts",
"dev": "bun run build.ts",
"lint": "prettier --write ./src",
"clean": "rm -rf dist .turbo node_modules .turbo-tsconfig.json tsconfig.tsbuildinfo",
"format": "prettier --write ./src",
"format:check": "prettier --check ./src",
"test": "bun test",
"postinstall": "bun scripts/install-ollama.js"
"postinstall": "bun scripts/install-ollama.js",
"prepare-publish": "bun run scripts/rewrite-core-version.ts",
"restore-workspace": "bun run scripts/rewrite-core-version.ts --restore",
"prepublishOnly": "bun run scripts/rewrite-core-version.ts",
"install-git-hooks": "bun run scripts/install-git-hooks.ts"
},
"publishCoreVersion": "1.7.2",
"publishConfig": {
"access": "public"
},
Expand Down
11 changes: 11 additions & 0 deletions scripts/git-hooks/post-checkout
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Post-checkout: in a monorepo, set @elizaos/core back to workspace:* so dev uses local core.
# Skip when this is the standalone repo (no workspace to link).
set -e
ROOT="$(git rev-parse --show-toplevel)"
if [ -f "$ROOT/packages/plugin-ollama/package.json" ] && grep -q '"publishCoreVersion"' "$ROOT/packages/plugin-ollama/package.json" 2>/dev/null; then
PLUGIN_DIR="$ROOT/packages/plugin-ollama"
(cd "$PLUGIN_DIR" && bun run scripts/rewrite-core-version.ts --restore)
fi
# If repo root has publishCoreVersion (standalone), do not restore — keep committed version for installs.
exit 0
13 changes: 13 additions & 0 deletions scripts/git-hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Pre-commit: ensure @elizaos/core is the verified version from publishCoreVersion so we commit the publishable state.
set -e
ROOT="$(git rev-parse --show-toplevel)"
if [ -f "$ROOT/packages/plugin-ollama/package.json" ] && grep -q '"publishCoreVersion"' "$ROOT/packages/plugin-ollama/package.json" 2>/dev/null; then
PLUGIN_DIR="$ROOT/packages/plugin-ollama"
else
[ -f "$ROOT/package.json" ] && grep -q '"publishCoreVersion"' "$ROOT/package.json" 2>/dev/null || exit 0
PLUGIN_DIR="$ROOT"
fi
(cd "$PLUGIN_DIR" && bun run scripts/rewrite-core-version.ts)
git add "$PLUGIN_DIR/package.json"
exit 0
28 changes: 28 additions & 0 deletions scripts/install-git-hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bun
/**
* Install git hooks so that:
* - pre-commit: commit with @elizaos/core = publishCoreVersion (verified for publish)
* - post-checkout: in monorepo, restore workspace:* for local dev
*
* Run from plugin dir: bun run scripts/install-git-hooks.ts
* Uses git rev-parse to find .git (works when plugin is in monorepo or standalone).
*/
import { $ } from "bun";
import { mkdir, writeFile } from "fs/promises";
import { join } from "path";

const pluginDir = join(import.meta.dir, "..");
const gitDir = (await $`git -C "${pluginDir}" rev-parse --git-dir`.text()).trim();
const hooksDir = join(pluginDir, gitDir, "hooks");
const hooksSource = join(pluginDir, "scripts", "git-hooks");

await mkdir(hooksDir, { recursive: true });

for (const name of ["pre-commit", "post-checkout"]) {
const src = join(hooksSource, name);
const dest = join(hooksDir, name);
const content = await Bun.file(src).text();
await writeFile(dest, content, { mode: 0o755 });
console.log(`Installed ${name} -> ${dest}`);
}
console.log("Git hooks installed. Pre-commit will use publishCoreVersion; post-checkout will restore workspace:* in monorepo.");
41 changes: 41 additions & 0 deletions scripts/rewrite-core-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bun
/**
* For standalone publish: replace workspace:* with a real @elizaos/core version
* so installs work outside the monorepo. Run after syncing from monorepo or in
* prepublishOnly. Use --restore to set back to workspace:* (e.g. after publish).
*
* Usage:
* bun run scripts/rewrite-core-version.ts # set to publish version
* bun run scripts/rewrite-core-version.ts --restore # set back to workspace:*
*
* Version: from package.json "publishCoreVersion" or env CORE_VERSION or "1.7.2"
*/

const RESTORE = process.argv.includes("--restore");
const pkgPath = new URL("../package.json", import.meta.url);
const pkg = (await Bun.file(pkgPath).json()) as Record<string, unknown> & {
dependencies?: Record<string, string>;
publishCoreVersion?: string;
};
const coreVersion =
process.env.CORE_VERSION ?? pkg.publishCoreVersion ?? "1.7.2";
const deps = pkg.dependencies ?? {};
const current = deps["@elizaos/core"];
Comment on lines +22 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Bug: Modifications to deps won't persist if pkg.dependencies is undefined.

If pkg.dependencies is undefined, the nullish coalescing creates a new empty object that isn't connected to pkg. Changes to deps won't be reflected in the written file.

🐛 Proposed fix
-const deps = pkg.dependencies ?? {};
+pkg.dependencies = pkg.dependencies ?? {};
+const deps = pkg.dependencies;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/rewrite-core-version.ts` around lines 22 - 23, The bug is that using
"const deps = pkg.dependencies ?? {}" creates a disconnected object when
pkg.dependencies is undefined so subsequent changes to deps won't be written
back; fix by ensuring pkg.dependencies is initialized on pkg before mutating:
explicitly set pkg.dependencies = pkg.dependencies ?? {} and then use const deps
= pkg.dependencies (or mutate pkg.dependencies directly) so modifications to
deps/current are persisted when writing the package back.


if (RESTORE) {
if (current !== coreVersion) {
console.log(`@elizaos/core is "${current}", not "${coreVersion}", skipping restore`);
process.exit(0);
}
deps["@elizaos/core"] = "workspace:*";
console.log('Set @elizaos/core to "workspace:*"');
} else {
if (current === coreVersion) {
console.log(`@elizaos/core already "${coreVersion}"`);
process.exit(0);
}
deps["@elizaos/core"] = coreVersion;
console.log(`Set @elizaos/core to "${coreVersion}"`);
}

await Bun.write(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
45 changes: 44 additions & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
import { describe, it, expect } from 'bun:test';
import { ollamaPlugin } from './index';
import { createOllama } from 'ollama-ai-provider';

// Mocked runtime for testing
const mockRuntime = {
emit: jest.fn(),
getSetting: jest.fn().mockReturnValue(null),
};

const mockModel = jest.fn().mockReturnValue({
doStream: jest.fn(),
});

const mockStreamText = jest.fn().mockImplementation(() => ({
textStream: (async function*() {
yield 'Hello, ';
yield 'world!';
})(),
}));

// Replace actual streamText with mock
jest.mock('ai', () => ({
streamText: mockStreamText,
}));
Comment on lines +6 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Jest APIs used in Bun test file - tests will fail.

This file imports from bun:test but uses jest.fn(), jest.mock(), and jest.spyOn() which are Jest-specific APIs not available in Bun's test runtime.

Bun's test framework uses different mocking approaches:

  • Use mock() from bun:test instead of jest.fn()
  • Use mock.module() for module mocking instead of jest.mock()
♻️ Example fix using Bun's mocking API
-import { describe, it, expect } from 'bun:test';
+import { describe, it, expect, mock, spyOn } from 'bun:test';
 import { ollamaPlugin } from './index';
 import { createOllama } from 'ollama-ai-provider';
 
 // Mocked runtime for testing
 const mockRuntime = {
-  emit: jest.fn(),
-  getSetting: jest.fn().mockReturnValue(null),
+  emit: mock(() => {}),
+  getSetting: mock(() => null),
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/index.test.ts` around lines 6 - 25, The test uses Jest-specific APIs
(jest.fn, jest.mock, jest.spyOn) which will fail in Bun; replace them with Bun
test mocks: change jest.fn() calls for mockRuntime.emit/getSetting and mockModel
and mockStreamText to use mock() from bun:test (preserve the same mock behavior
and mockImplementation semantics), and replace the module mock jest.mock('ai',
...) with Bun's mock.module or mockModule equivalent to stub 'ai' so streamText
returns the async generator; also convert any jest.spyOn usages (if present
elsewhere) to Bun's spy/mocking utilities so tests run under Bun. Ensure you
update symbols mockRuntime, mockModel, and mockStreamText and the module mock
for 'ai' accordingly.


describe('ollamaPlugin', () => {
it('should export ollamaPlugin', () => {
Expand All @@ -15,7 +38,27 @@ describe('ollamaPlugin', () => {
expect(typeof ollamaPlugin.init).toBe('function');
});

it('should have providers array', () => {
it('should emit text stream events in order', async () => {
const result = await ollamaPlugin.models[ModelType.TEXT_SMALL](mockRuntime, {
prompt: 'Test prompt',
});

expect(mockRuntime.emit).toHaveBeenCalledWith('textStream', 'Hello, ');
expect(mockRuntime.emit).toHaveBeenCalledWith('textStream', 'world!');
expect(mockRuntime.emit).toHaveBeenCalledWith('textStreamDone', true);
Comment on lines +41 to +48
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

ModelType is not imported - will cause ReferenceError.

The test references ModelType.TEXT_SMALL but ModelType is never imported. Add the import from @elizaos/core:

 import { describe, it, expect } from 'bun:test';
 import { ollamaPlugin } from './index';
 import { createOllama } from 'ollama-ai-provider';
+import { ModelType } from '@elizaos/core';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/index.test.ts` around lines 41 - 48, The test references ModelType (used
as ModelType.TEXT_SMALL) but it is not imported, causing a ReferenceError; add
an import for ModelType from "@elizaos/core" at the top of the file (where other
imports live) so the symbol ModelType is available for the test; update
src/index.test.ts to import { ModelType } and run the test to verify the error
is resolved.

});

it('should handle timeout behavior correctly', async () => {
const timeoutModel = createOllama({});
const generateOllamaTextSpy = jest.spyOn(timeoutModel, 'doStream');

await ollamaPlugin.models[ModelType.TEXT_SMALL](mockRuntime, {
prompt: 'Test prompt with timeout',
});

expect(generateOllamaTextSpy).toHaveBeenCalled();
expect(mockRuntime.emit).toHaveBeenCalledWith('textStreamDone', true);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orphaned test code creates syntax error from bad merge

High Severity

The it('should have providers array', () => { line was removed and replaced with two new test blocks, but the body of the old test (the expect calls at lines 62–63) and its closing }); at line 64 were left behind. This leaves bare expect calls outside any it() block and creates an unmatched }); that makes the file a syntax error. bun test will fail to parse this file entirely.

Fix in Cursor Fix in Web

expect(ollamaPlugin.providers).toBeDefined();
expect(Array.isArray(ollamaPlugin.providers)).toBe(true);
});
Expand Down
Loading