Files
wifi-densepose/vendor/ruvector/npm/packages/ruvllm/test/features.test.js

295 lines
7.9 KiB
JavaScript

/**
* Tests for new features: Sessions, Streaming, SONA
*/
const { test, describe } = require('node:test');
const assert = require('node:assert');
const {
RuvLLM,
SessionManager,
StreamingGenerator,
SonaCoordinator,
TrajectoryBuilder,
ReasoningBank,
EwcManager,
} = require('../dist/cjs/index.js');
describe('SessionManager', () => {
test('should create session', () => {
const llm = new RuvLLM();
const sessions = new SessionManager(llm);
const session = sessions.create({ userId: 'test' });
assert.ok(session.id.startsWith('session-'));
assert.strictEqual(session.messageCount, 0);
assert.deepStrictEqual(session.metadata, { userId: 'test' });
});
test('should chat with context', () => {
const llm = new RuvLLM();
const sessions = new SessionManager(llm);
const session = sessions.create();
const response1 = sessions.chat(session.id, 'Hello');
const response2 = sessions.chat(session.id, 'How are you?');
assert.strictEqual(session.messages.length, 4); // 2 user + 2 assistant
assert.ok(response1.text);
assert.ok(response2.text);
});
test('should get history', () => {
const llm = new RuvLLM();
const sessions = new SessionManager(llm);
const session = sessions.create();
sessions.chat(session.id, 'Message 1');
sessions.chat(session.id, 'Message 2');
const history = sessions.getHistory(session.id);
assert.strictEqual(history.length, 4);
const limited = sessions.getHistory(session.id, 2);
assert.strictEqual(limited.length, 2);
});
test('should export and import session', () => {
const llm = new RuvLLM();
const sessions = new SessionManager(llm);
const session = sessions.create({ key: 'value' });
sessions.chat(session.id, 'Test message');
const exported = sessions.export(session.id);
assert.ok(exported);
const imported = sessions.import(exported);
assert.strictEqual(imported.id, session.id);
assert.strictEqual(imported.messages.length, 2);
});
test('should end session', () => {
const llm = new RuvLLM();
const sessions = new SessionManager(llm);
const session = sessions.create();
assert.ok(sessions.get(session.id));
sessions.end(session.id);
assert.strictEqual(sessions.get(session.id), undefined);
});
});
describe('StreamingGenerator', () => {
test('should stream response', async () => {
const llm = new RuvLLM();
const streamer = new StreamingGenerator(llm);
const chunks = [];
for await (const chunk of streamer.stream('Test prompt')) {
chunks.push(chunk);
}
assert.ok(chunks.length > 0);
assert.ok(chunks[chunks.length - 1].done);
});
test('should collect stream', async () => {
const llm = new RuvLLM();
const streamer = new StreamingGenerator(llm);
const result = await streamer.collect('Test prompt');
assert.ok(typeof result === 'string');
});
test('should use callbacks', async () => {
const llm = new RuvLLM();
const streamer = new StreamingGenerator(llm);
let chunkCount = 0;
let completed = false;
await streamer.streamWithCallbacks('Test', {
onChunk: () => chunkCount++,
onComplete: () => { completed = true; },
});
assert.ok(chunkCount > 0);
assert.ok(completed);
});
});
describe('TrajectoryBuilder', () => {
test('should build trajectory', () => {
const builder = new TrajectoryBuilder();
const trajectory = builder
.startStep('query', 'What is AI?')
.endStep('AI is...', 0.95)
.startStep('memory', 'searching')
.endStep('found 3 results', 0.88)
.complete('success');
assert.ok(trajectory.id.startsWith('traj-'));
assert.strictEqual(trajectory.steps.length, 2);
assert.strictEqual(trajectory.outcome, 'success');
assert.ok(trajectory.durationMs >= 0);
});
test('should track step durations', () => {
const builder = new TrajectoryBuilder();
builder.startStep('query', 'input');
// Small delay
const start = Date.now();
while (Date.now() - start < 5) { /* wait */ }
builder.endStep('output', 0.9);
const trajectory = builder.complete('success');
assert.ok(trajectory.steps[0].durationMs >= 0);
});
});
describe('ReasoningBank', () => {
test('should store and retrieve patterns', () => {
const bank = new ReasoningBank(0.5); // Lower threshold for testing
const embedding = [0.1, 0.2, 0.3, 0.4, 0.5];
const id = bank.store('query_response', embedding);
assert.ok(id.startsWith('pat-'));
const pattern = bank.get(id);
assert.ok(pattern);
assert.strictEqual(pattern.type, 'query_response');
assert.strictEqual(pattern.successRate, 1.0);
});
test('should find similar patterns', () => {
const bank = new ReasoningBank(0.5);
const emb1 = [1, 0, 0, 0, 0];
const emb2 = [0.9, 0.1, 0, 0, 0]; // Similar to emb1
bank.store('query_response', emb1);
bank.store('routing', emb2);
const similar = bank.findSimilar([1, 0, 0, 0, 0], 5);
assert.ok(similar.length >= 1);
});
test('should track usage', () => {
const bank = new ReasoningBank();
const embedding = [0.1, 0.2, 0.3];
const id = bank.store('query_response', embedding);
bank.recordUsage(id, true);
bank.recordUsage(id, true);
bank.recordUsage(id, false);
const pattern = bank.get(id);
assert.strictEqual(pattern.useCount, 3);
assert.ok(pattern.successRate < 1.0);
});
test('should provide stats', () => {
const bank = new ReasoningBank();
bank.store('query_response', [0.1, 0.2]);
bank.store('routing', [0.3, 0.4]);
const stats = bank.stats();
assert.strictEqual(stats.totalPatterns, 2);
assert.strictEqual(stats.byType['query_response'], 1);
assert.strictEqual(stats.byType['routing'], 1);
});
});
describe('EwcManager', () => {
test('should register tasks', () => {
const ewc = new EwcManager(1000);
ewc.registerTask('task1', [0.1, 0.2, 0.3]);
ewc.registerTask('task2', [0.4, 0.5, 0.6]);
const stats = ewc.stats();
assert.strictEqual(stats.tasksLearned, 2);
assert.strictEqual(stats.fisherComputed, true);
});
test('should compute penalty', () => {
const ewc = new EwcManager(1000);
ewc.registerTask('task1', [0.5, 0.5, 0.5]);
// Weights that differ from optimal should have higher penalty
const penalty1 = ewc.computePenalty([0.5, 0.5, 0.5]);
const penalty2 = ewc.computePenalty([1.0, 1.0, 1.0]);
assert.ok(penalty2 > penalty1);
});
});
describe('SonaCoordinator', () => {
test('should create with config', () => {
const sona = new SonaCoordinator({
instantLoopEnabled: true,
ewcLambda: 5000,
});
assert.ok(sona);
const stats = sona.stats();
assert.ok(stats.patterns);
assert.ok(stats.ewc);
});
test('should record signals', () => {
const sona = new SonaCoordinator();
sona.recordSignal({
requestId: 'req-123',
quality: 0.9,
type: 'positive',
timestamp: new Date(),
});
const stats = sona.stats();
assert.strictEqual(stats.signalsReceived, 1);
});
test('should record trajectories', () => {
const sona = new SonaCoordinator();
const builder = new TrajectoryBuilder();
const trajectory = builder
.startStep('query', 'test')
.endStep('response', 0.95)
.complete('success');
sona.recordTrajectory(trajectory);
const stats = sona.stats();
assert.strictEqual(stats.trajectoriesBuffered, 1);
});
test('should run background loop', () => {
const sona = new SonaCoordinator();
// Add some trajectories
for (let i = 0; i < 3; i++) {
const builder = new TrajectoryBuilder();
const trajectory = builder
.startStep('query', `test ${i}`)
.endStep(`response ${i}`, 0.95)
.complete('success');
sona.recordTrajectory(trajectory);
}
const result = sona.runBackgroundLoop();
assert.strictEqual(result.trajectoriesProcessed, 3);
});
});