Skip to main content
Glama
bias-learning-system.test.ts17.7 kB
/** * Tests for BiasLearningSystem * * Tests the learning system that improves bias detection accuracy through * user feedback integration, adaptive pattern recognition, and personalized * sensitivity adjustment. */ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { BiasLearningSystem } from "../../../bias/bias-learning-system"; import type { BiasFeedback, DetectedBias } from "../../../bias/types"; import { BiasType } from "../../../bias/types"; // Test fixtures and helpers function createMockDetectedBias(overrides?: Partial<DetectedBias>): DetectedBias { return { type: BiasType.CONFIRMATION, severity: 0.7, confidence: 0.75, evidence: ["Only supporting evidence considered"], location: { stepIndex: 0, reasoning: "Test reasoning", }, explanation: "Test explanation", detectedAt: new Date(), ...overrides, }; } function createMockFeedback(overrides?: Partial<BiasFeedback>): BiasFeedback { return { detectedBias: createMockDetectedBias(), correct: true, userId: "test-user", timestamp: new Date(), ...overrides, }; } // Removed unused createMockReasoningChain function describe("BiasLearningSystem", () => { let learningSystem: BiasLearningSystem; beforeEach(() => { learningSystem = new BiasLearningSystem(); }); afterEach(() => { // Cleanup }); describe("User Feedback Integration", () => { it("should record user feedback on bias detections", () => { const feedback = createMockFeedback({ correct: true }); learningSystem.integrateFeedback(feedback); const metrics = learningSystem.getAccuracyMetrics(); expect(metrics.truePositives).toBe(1); }); it("should store and retrieve feedback by user ID", () => { const feedback1 = createMockFeedback({ userId: "user1", correct: true }); const feedback2 = createMockFeedback({ userId: "user2", correct: false }); learningSystem.integrateFeedback(feedback1); learningSystem.integrateFeedback(feedback2); const sensitivity1 = learningSystem.getUserSensitivity("user1", BiasType.CONFIRMATION); const sensitivity2 = learningSystem.getUserSensitivity("user2", BiasType.CONFIRMATION); expect(sensitivity1).toBeGreaterThanOrEqual(0); expect(sensitivity2).toBeGreaterThanOrEqual(0); }); it("should aggregate feedback by bias type", () => { const feedback1 = createMockFeedback({ detectedBias: createMockDetectedBias({ type: BiasType.CONFIRMATION }), correct: true, }); const feedback2 = createMockFeedback({ detectedBias: createMockDetectedBias({ type: BiasType.ANCHORING }), correct: true, }); learningSystem.integrateFeedback(feedback1); learningSystem.integrateFeedback(feedback2); const confirmationMetrics = learningSystem.getAccuracyMetrics(BiasType.CONFIRMATION); const anchoringMetrics = learningSystem.getAccuracyMetrics(BiasType.ANCHORING); expect(confirmationMetrics.truePositives).toBe(1); expect(anchoringMetrics.truePositives).toBe(1); }); it("should influence detection thresholds based on feedback", () => { const correctFeedback = createMockFeedback({ correct: true }); learningSystem.integrateFeedback(correctFeedback); learningSystem.integrateFeedback(correctFeedback); learningSystem.integrateFeedback(correctFeedback); // After multiple correct feedbacks, system should have learned const metrics = learningSystem.getAccuracyMetrics(); expect(metrics.truePositives).toBe(3); expect(metrics.precision).toBe(1.0); }); it("should handle invalid feedback data gracefully", () => { const invalidFeedback = { detectedBias: null as any, correct: true, userId: "test", timestamp: new Date(), }; expect(() => learningSystem.integrateFeedback(invalidFeedback)).toThrow(); }); it("should handle concurrent feedback from multiple users", () => { const users = ["user1", "user2", "user3"]; for (const userId of users) { const feedback = createMockFeedback({ userId, correct: true }); learningSystem.integrateFeedback(feedback); } const metrics = learningSystem.getLearningMetrics(); expect(metrics.userCount).toBe(0); // Users are created on sensitivity adjustment expect(metrics.totalFeedback).toBe(3); }); }); describe("Adaptive Pattern Recognition", () => { it("should learn patterns from feedback data", () => { const feedbackItems = [ createMockFeedback({ correct: false }), createMockFeedback({ correct: false }), createMockFeedback({ correct: false }), ]; const pattern = learningSystem.learnNewPattern(feedbackItems); expect(pattern).not.toBeNull(); if (pattern) { expect(pattern.frequency).toBeGreaterThanOrEqual(2); } }); it("should return null when learning pattern with insufficient data (<3 items)", () => { const feedbackItems = [ createMockFeedback({ correct: false }), createMockFeedback({ correct: false }), ]; const pattern = learningSystem.learnNewPattern(feedbackItems); expect(pattern).toBeNull(); }); it("should adjust pattern weights based on accuracy", () => { learningSystem.updatePatternWeights(BiasType.CONFIRMATION, 0.9); // Weight should be adjusted based on high accuracy // We can't directly access weights, but we can verify no errors expect(() => learningSystem.updatePatternWeights(BiasType.CONFIRMATION, 0.9)).not.toThrow(); }); it("should increase pattern weight for high accuracy (>0.8)", () => { // High accuracy should increase weight learningSystem.updatePatternWeights(BiasType.CONFIRMATION, 0.85); expect(() => learningSystem.updatePatternWeights(BiasType.CONFIRMATION, 0.85)).not.toThrow(); }); it("should decrease pattern weight for low accuracy (<0.6)", () => { // Low accuracy should decrease weight learningSystem.updatePatternWeights(BiasType.ANCHORING, 0.5); expect(() => learningSystem.updatePatternWeights(BiasType.ANCHORING, 0.5)).not.toThrow(); }); it("should discover new patterns from false negatives", () => { const falseNegatives = [ createMockFeedback({ correct: false }), createMockFeedback({ correct: false }), createMockFeedback({ correct: false }), ]; const pattern = learningSystem.learnNewPattern(falseNegatives); expect(pattern).not.toBeNull(); }); it("should prune patterns that cause false positives", () => { // Add false positive feedback for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: false })); } learningSystem.pruneIneffectivePatterns(); // System should have adjusted weights const metrics = learningSystem.getAccuracyMetrics(); expect(metrics.falsePositives).toBe(5); }); it("should track pattern effectiveness over time", () => { // Add feedback over time for (let i = 0; i < 10; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: i % 2 === 0 })); } const metrics = learningSystem.getAccuracyMetrics(); expect(metrics.truePositives).toBe(5); expect(metrics.falsePositives).toBe(5); }); }); describe("Detection Accuracy Improvement", () => { it("should measure baseline accuracy", () => { const metrics = learningSystem.getAccuracyMetrics(); expect(metrics.truePositives).toBe(0); expect(metrics.falsePositives).toBe(0); expect(metrics.precision).toBe(0); }); it("should track accuracy improvement over time with feedback", () => { // Add early feedback (lower accuracy) learningSystem.integrateFeedback( createMockFeedback({ correct: true, timestamp: new Date(Date.now() - 1000 * 60 * 60 * 24 * 8), }) ); learningSystem.integrateFeedback( createMockFeedback({ correct: false, timestamp: new Date(Date.now() - 1000 * 60 * 60 * 24 * 8), }) ); // Add recent feedback (higher accuracy) learningSystem.integrateFeedback(createMockFeedback({ correct: true })); learningSystem.integrateFeedback(createMockFeedback({ correct: true })); const improvement = learningSystem.getImprovementRate("week"); expect(improvement).toBeGreaterThanOrEqual(0); }); it("should track accuracy per bias type", () => { learningSystem.integrateFeedback( createMockFeedback({ detectedBias: createMockDetectedBias({ type: BiasType.CONFIRMATION }), correct: true, }) ); learningSystem.integrateFeedback( createMockFeedback({ detectedBias: createMockDetectedBias({ type: BiasType.ANCHORING }), correct: false, }) ); const confirmationMetrics = learningSystem.getAccuracyMetrics(BiasType.CONFIRMATION); const anchoringMetrics = learningSystem.getAccuracyMetrics(BiasType.ANCHORING); expect(confirmationMetrics.precision).toBe(1.0); expect(anchoringMetrics.precision).toBe(0.0); }); it("should achieve >70% overall detection rate", () => { // Add feedback with >70% accuracy for (let i = 0; i < 10; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: i < 8 })); } const metrics = learningSystem.getAccuracyMetrics(); expect(metrics.precision).toBeGreaterThanOrEqual(0.7); }); it("should calculate precision, recall, and F1 score", () => { learningSystem.integrateFeedback(createMockFeedback({ correct: true })); learningSystem.integrateFeedback(createMockFeedback({ correct: true })); learningSystem.integrateFeedback(createMockFeedback({ correct: false })); const metrics = learningSystem.getAccuracyMetrics(); expect(metrics.precision).toBeCloseTo(0.667, 2); expect(metrics.recall).toBe(1.0); // No false negatives tracked expect(metrics.f1Score).toBeGreaterThan(0); }); }); describe("Personalized Sensitivity Adjustment", () => { it("should store user-specific sensitivity preferences", () => { learningSystem.adjustSensitivity("user1", BiasType.CONFIRMATION, 0.2); const sensitivity = learningSystem.getUserSensitivity("user1", BiasType.CONFIRMATION); expect(sensitivity).toBe(0.7); // 0.5 default + 0.2 }); it("should automatically tune sensitivity based on feedback", () => { // Add multiple correct feedbacks for a user for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback(createMockFeedback({ userId: "user1", correct: true })); } // Sensitivity should be adjusted automatically const sensitivity = learningSystem.getUserSensitivity("user1", BiasType.CONFIRMATION); expect(sensitivity).toBeGreaterThanOrEqual(0); }); it("should adjust sensitivity per bias type", () => { learningSystem.adjustSensitivity("user1", BiasType.CONFIRMATION, 0.1); learningSystem.adjustSensitivity("user1", BiasType.ANCHORING, -0.1); const confirmationSensitivity = learningSystem.getUserSensitivity( "user1", BiasType.CONFIRMATION ); const anchoringSensitivity = learningSystem.getUserSensitivity("user1", BiasType.ANCHORING); expect(confirmationSensitivity).toBeGreaterThan(anchoringSensitivity); }); it("should persist sensitivity across sessions", () => { learningSystem.adjustSensitivity("user1", BiasType.CONFIRMATION, 0.3); const sensitivity1 = learningSystem.getUserSensitivity("user1", BiasType.CONFIRMATION); const sensitivity2 = learningSystem.getUserSensitivity("user1", BiasType.CONFIRMATION); expect(sensitivity1).toBe(sensitivity2); }); it("should enforce sensitivity bounds [0, 1]", () => { learningSystem.adjustSensitivity("user1", BiasType.CONFIRMATION, 2.0); const high = learningSystem.getUserSensitivity("user1", BiasType.CONFIRMATION); expect(high).toBeLessThanOrEqual(1.0); learningSystem.adjustSensitivity("user1", BiasType.CONFIRMATION, -5.0); const low = learningSystem.getUserSensitivity("user1", BiasType.CONFIRMATION); expect(low).toBeGreaterThanOrEqual(0.0); }); }); describe("Learning Effectiveness", () => { it("should measure learning rate", () => { for (let i = 0; i < 20; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: i > 5 })); } const improvement = learningSystem.getImprovementRate("all"); expect(improvement).toBeGreaterThanOrEqual(0); }); it("should return 0 improvement rate when insufficient data (<10 feedback items)", () => { // Add only 5 feedback items for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: true })); } const improvement = learningSystem.getImprovementRate("all"); expect(improvement).toBe(0); }); it("should calculate improvement rate for day period", () => { // Add feedback from yesterday for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback( createMockFeedback({ correct: false, timestamp: new Date(Date.now() - 1000 * 60 * 60 * 25), // 25 hours ago }) ); } // Add recent feedback (today) for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: true })); } const improvement = learningSystem.getImprovementRate("day"); expect(improvement).toBeGreaterThan(0); }); it("should calculate improvement rate for week period", () => { // Add feedback from 8 days ago for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback( createMockFeedback({ correct: false, timestamp: new Date(Date.now() - 1000 * 60 * 60 * 24 * 8), }) ); } // Add recent feedback (this week) for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: true })); } const improvement = learningSystem.getImprovementRate("week"); expect(improvement).toBeGreaterThan(0); }); it("should calculate improvement rate for month period", () => { // Add feedback from 31 days ago for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback( createMockFeedback({ correct: false, timestamp: new Date(Date.now() - 1000 * 60 * 60 * 24 * 31), }) ); } // Add recent feedback (this month) for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: true })); } const improvement = learningSystem.getImprovementRate("month"); expect(improvement).toBeGreaterThan(0); }); it("should converge to optimal thresholds", () => { // Simulate learning over time for (let i = 0; i < 50; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: Math.random() > 0.3 })); } const learningMetrics = learningSystem.getLearningMetrics(); expect(learningMetrics.totalFeedback).toBeGreaterThan(0); }); it("should calculate learning metrics with baseline accuracy", () => { // First, establish baseline by getting initial metrics const initialMetrics = learningSystem.getLearningMetrics(); expect(initialMetrics.totalFeedback).toBe(0); expect(initialMetrics.accuracyImprovement).toBe(0); // Add feedback to improve accuracy for (let i = 0; i < 20; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: true })); } const improvedMetrics = learningSystem.getLearningMetrics(); expect(improvedMetrics.totalFeedback).toBe(20); expect(improvedMetrics.accuracyImprovement).toBeGreaterThanOrEqual(0); }); it("should adapt to user preferences", () => { learningSystem.adjustSensitivity("user1", BiasType.CONFIRMATION, 0.2); learningSystem.adjustSensitivity("user2", BiasType.CONFIRMATION, -0.2); const sensitivity1 = learningSystem.getUserSensitivity("user1", BiasType.CONFIRMATION); const sensitivity2 = learningSystem.getUserSensitivity("user2", BiasType.CONFIRMATION); expect(sensitivity1).toBeGreaterThan(sensitivity2); }); it("should track improvement metrics (false positive/negative reduction)", () => { // Early period with more false positives for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback( createMockFeedback({ correct: false, timestamp: new Date(Date.now() - 1000 * 60 * 60 * 24 * 8), }) ); } // Recent period with fewer false positives for (let i = 0; i < 5; i++) { learningSystem.integrateFeedback(createMockFeedback({ correct: true })); } const improvement = learningSystem.getImprovementRate("week"); expect(improvement).toBeGreaterThan(0); }); it("should complete feedback integration in <10ms", () => { const feedback = createMockFeedback(); const start = performance.now(); learningSystem.integrateFeedback(feedback); const duration = performance.now() - start; expect(duration).toBeLessThan(10); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server