Skip to content

Commit f2a4ffd

Browse files
grypezclaude
andcommitted
test(kernel-test-local): integration test for LMS service through the kernel
Add lms-kernel.test.ts: launches a subcluster with a bundled lms-chat-vat, registers an Open /v1 kernel service backed by Ollama at localhost:11434, and asserts the vat receives a chat response. Wire via test:integration script and vitest.config.integration.ts. Update package deps and tsconfig references for the @ocap/nodejs → @metamask/kernel-node-runtime rename. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c410257 commit f2a4ffd

6 files changed

Lines changed: 173 additions & 1 deletion

File tree

packages/kernel-test-local/package.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
},
1414
"type": "module",
1515
"scripts": {
16-
"clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./.turbo ./logs",
16+
"build": "ocap bundle test/e2e/vats",
17+
"clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./.turbo ./logs './test/e2e/vats/*.bundle'",
1718
"lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies",
1819
"lint:dependencies": "depcheck --quiet",
1920
"lint:eslint": "eslint . --cache",
@@ -22,14 +23,20 @@
2223
"build:docs": "typedoc",
2324
"test": "vitest run --config vitest.config.ts",
2425
"test:e2e:local": "vitest run --config vitest.config.e2e.ts",
26+
"test:integration": "vitest run --config vitest.config.integration.ts",
2527
"test:clean": "yarn test --no-cache --coverage.clean",
2628
"test:dev": "yarn test --mode development",
2729
"test:verbose": "yarn test --reporter verbose",
2830
"test:watch": "vitest --config vitest.config.ts",
2931
"test:dev:quiet": "yarn test:dev --reporter @ocap/repo-tools/vitest-reporters/silent"
3032
},
3133
"dependencies": {
34+
"@endo/eventual-send": "^1.3.4",
35+
"@metamask/kernel-node-runtime": "workspace:^",
36+
"@metamask/kernel-store": "workspace:^",
37+
"@metamask/kernel-utils": "workspace:^",
3238
"@metamask/logger": "workspace:^",
39+
"@metamask/ocap-kernel": "workspace:^",
3340
"@ocap/kernel-agents": "workspace:^",
3441
"@ocap/kernel-language-model-service": "workspace:^",
3542
"@ocap/repo-tools": "workspace:^"
@@ -39,6 +46,8 @@
3946
"@metamask/eslint-config": "^15.0.0",
4047
"@metamask/eslint-config-nodejs": "^15.0.0",
4148
"@metamask/eslint-config-typescript": "^15.0.0",
49+
"@metamask/kernel-cli": "workspace:^",
50+
"@metamask/kernel-shims": "workspace:^",
4251
"@ocap/kernel-agents-repl": "workspace:^",
4352
"@types/node": "^22.13.1",
4453
"@typescript-eslint/eslint-plugin": "^8.29.0",
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { NodejsPlatformServices } from '@metamask/kernel-node-runtime';
2+
import { makeSQLKernelDatabase } from '@metamask/kernel-store/sqlite/nodejs';
3+
import { waitUntilQuiescent } from '@metamask/kernel-utils';
4+
import {
5+
Logger,
6+
makeArrayTransport,
7+
makeConsoleTransport,
8+
} from '@metamask/logger';
9+
import type { LogEntry } from '@metamask/logger';
10+
import { Kernel } from '@metamask/ocap-kernel';
11+
import {
12+
LANGUAGE_MODEL_SERVICE_NAME,
13+
makeKernelLanguageModelService,
14+
makeOpenV1NodejsService,
15+
} from '@ocap/kernel-language-model-service';
16+
import { fetchMock } from '@ocap/repo-tools/test-utils/fetch-mock';
17+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
18+
19+
import { DEFAULT_MODEL } from '../../src/constants.ts';
20+
import { filterTransports } from '../../src/utils.ts';
21+
22+
const getBundleSpec = (name: string): string =>
23+
new URL(`./vats/${name}.bundle`, import.meta.url).toString();
24+
25+
describe.sequential('lms-kernel', () => {
26+
beforeAll(() => {
27+
fetchMock.disableMocks();
28+
});
29+
30+
afterAll(() => {
31+
fetchMock.enableMocks();
32+
});
33+
34+
it(
35+
'sends a chat message through the kernel to Ollama and receives a response',
36+
{ timeout: 60_000 },
37+
async () => {
38+
const kernelDatabase = await makeSQLKernelDatabase({
39+
dbFilename: ':memory:',
40+
});
41+
42+
const entries: LogEntry[] = [];
43+
const logger = new Logger({
44+
transports: [
45+
filterTransports(makeConsoleTransport(), makeArrayTransport(entries)),
46+
],
47+
});
48+
49+
const platformServices = new NodejsPlatformServices({
50+
logger: logger.subLogger({ tags: ['vat-worker-manager'] }),
51+
});
52+
53+
const kernel = await Kernel.make(platformServices, kernelDatabase, {
54+
resetStorage: true,
55+
logger,
56+
});
57+
58+
const { chat } = makeOpenV1NodejsService({
59+
endowments: { fetch },
60+
baseUrl: 'http://localhost:11434',
61+
apiKey: 'test-api-key',
62+
});
63+
const { name, service } = makeKernelLanguageModelService(chat);
64+
kernel.registerKernelServiceObject(name, service);
65+
66+
await kernel.launchSubcluster({
67+
bootstrap: 'main',
68+
services: [LANGUAGE_MODEL_SERVICE_NAME],
69+
vats: {
70+
main: {
71+
bundleSpec: getBundleSpec('lms-chat-vat'),
72+
parameters: { model: DEFAULT_MODEL },
73+
},
74+
},
75+
});
76+
await waitUntilQuiescent(100);
77+
78+
const responseEntry = entries.find((entry) =>
79+
entry.message?.startsWith('lms-chat response:'),
80+
);
81+
expect(responseEntry).toBeDefined();
82+
expect(responseEntry?.message?.length).toBeGreaterThan(
83+
'lms-chat response: '.length,
84+
);
85+
expect(responseEntry?.message).toMatch(
86+
/^lms-chat response: [hH]ello[.!]?$/u,
87+
);
88+
},
89+
);
90+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { ERef } from '@endo/eventual-send';
2+
import { makeDefaultExo } from '@metamask/kernel-utils/exo';
3+
import type { Logger } from '@metamask/logger';
4+
import { makeChatClient } from '@ocap/kernel-language-model-service';
5+
import type { ChatService } from '@ocap/kernel-language-model-service';
6+
7+
/**
8+
* A vat that uses a kernel language model service to perform a chat completion
9+
* and logs the response. Used by lms-kernel.test.ts to verify the full
10+
* kernel → LMS service → Ollama round-trip.
11+
*
12+
* @param vatPowers - Vat powers, expected to include a logger.
13+
* @param parameters - Vat parameters.
14+
* @param parameters.model - The model to use for chat completion.
15+
* @returns A default Exo instance.
16+
*/
17+
18+
export function buildRootObject(
19+
vatPowers: Record<string, unknown>,
20+
{ model }: { model: string },
21+
) {
22+
const logger = vatPowers.logger as Logger;
23+
const tlog = (message: string): void => {
24+
logger.subLogger({ tags: ['test', 'lms-chat'] }).log(message);
25+
};
26+
27+
return makeDefaultExo('root', {
28+
async bootstrap(
29+
_roots: unknown,
30+
{ languageModelService }: { languageModelService: ERef<ChatService> },
31+
) {
32+
const client = makeChatClient(languageModelService, model);
33+
const result = await client.chat.completions.create({
34+
messages: [
35+
{ role: 'user', content: 'Reply with exactly one word: hello.' },
36+
],
37+
});
38+
tlog(`lms-chat response: ${result.choices[0]?.message.content ?? ''}`);
39+
},
40+
});
41+
}

packages/kernel-test-local/tsconfig.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,22 @@
88
},
99
"references": [
1010
{ "path": "../kernel-agents" },
11+
{ "path": "../kernel-shims" },
1112
{ "path": "../kernel-agents-repl" },
1213
{ "path": "../kernel-language-model-service" },
14+
{ "path": "../kernel-store" },
15+
{ "path": "../kernel-utils" },
1316
{ "path": "../logger" },
17+
{ "path": "../ocap-kernel" },
18+
{ "path": "../kernel-node-runtime" },
1419
{ "path": "../repo-tools" }
1520
],
1621
"include": [
1722
"../../vitest.config.ts",
1823
"./src",
1924
"./vitest.config.ts",
2025
"./vitest.config.e2e.ts",
26+
"./vitest.config.integration.ts",
2127
"./test/e2e"
2228
]
2329
}

packages/kernel-test-local/vitest.config.e2e.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export default defineConfig((args) => {
1313
testTimeout: 30_000,
1414
hookTimeout: 10_000,
1515
include: ['./test/e2e/**/*.test.ts'],
16+
exclude: ['./test/e2e/lms-kernel.test.ts'],
1617
},
1718
}),
1819
);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { mergeConfig } from '@ocap/repo-tools/vitest-config';
2+
import { fileURLToPath } from 'node:url';
3+
import { defineConfig, defineProject } from 'vitest/config';
4+
5+
import defaultConfig from '../../vitest.config.ts';
6+
7+
export default defineConfig((args) => {
8+
return mergeConfig(
9+
args,
10+
defaultConfig,
11+
defineProject({
12+
test: {
13+
name: 'kernel-test-local-e2e-kernel',
14+
testTimeout: 60_000,
15+
hookTimeout: 10_000,
16+
setupFiles: [
17+
fileURLToPath(
18+
import.meta.resolve('@metamask/kernel-shims/endoify-node'),
19+
),
20+
],
21+
include: ['./test/e2e/lms-kernel.test.ts'],
22+
},
23+
}),
24+
);
25+
});

0 commit comments

Comments
 (0)