@holoscript/framework 6.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ALL-test-results.json +1 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/ROADMAP.md +175 -0
- package/dist/AgentManifest-CB4xM-Ma.d.cts +704 -0
- package/dist/AgentManifest-CB4xM-Ma.d.ts +704 -0
- package/dist/BehaviorTree-BrBFECv5.d.cts +103 -0
- package/dist/BehaviorTree-BrBFECv5.d.ts +103 -0
- package/dist/InvisibleWallet-BB6tFvRA.d.cts +1732 -0
- package/dist/InvisibleWallet-rtRrBOA8.d.ts +1732 -0
- package/dist/OrchestratorAgent-BvWgf9uw.d.cts +798 -0
- package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +798 -0
- package/dist/agents/index.cjs +4790 -0
- package/dist/agents/index.d.cts +1788 -0
- package/dist/agents/index.d.ts +1788 -0
- package/dist/agents/index.js +4695 -0
- package/dist/ai/index.cjs +5347 -0
- package/dist/ai/index.d.cts +1753 -0
- package/dist/ai/index.d.ts +1753 -0
- package/dist/ai/index.js +5244 -0
- package/dist/behavior.cjs +449 -0
- package/dist/behavior.d.cts +130 -0
- package/dist/behavior.d.ts +130 -0
- package/dist/behavior.js +407 -0
- package/dist/economy/index.cjs +3659 -0
- package/dist/economy/index.d.cts +747 -0
- package/dist/economy/index.d.ts +747 -0
- package/dist/economy/index.js +3617 -0
- package/dist/implementations-D9T3un9D.d.cts +236 -0
- package/dist/implementations-D9T3un9D.d.ts +236 -0
- package/dist/index.cjs +24550 -0
- package/dist/index.d.cts +1729 -0
- package/dist/index.d.ts +1729 -0
- package/dist/index.js +24277 -0
- package/dist/learning/index.cjs +219 -0
- package/dist/learning/index.d.cts +104 -0
- package/dist/learning/index.d.ts +104 -0
- package/dist/learning/index.js +189 -0
- package/dist/negotiation/index.cjs +970 -0
- package/dist/negotiation/index.d.cts +610 -0
- package/dist/negotiation/index.d.ts +610 -0
- package/dist/negotiation/index.js +931 -0
- package/dist/skills/index.cjs +1118 -0
- package/dist/skills/index.d.cts +289 -0
- package/dist/skills/index.d.ts +289 -0
- package/dist/skills/index.js +1079 -0
- package/dist/swarm/index.cjs +5268 -0
- package/dist/swarm/index.d.cts +2433 -0
- package/dist/swarm/index.d.ts +2433 -0
- package/dist/swarm/index.js +5221 -0
- package/dist/training/index.cjs +2745 -0
- package/dist/training/index.d.cts +1734 -0
- package/dist/training/index.d.ts +1734 -0
- package/dist/training/index.js +2687 -0
- package/extract-failures.js +10 -0
- package/package.json +82 -0
- package/src/__tests__/bounty-marketplace.test.ts +374 -0
- package/src/__tests__/delegation.test.ts +144 -0
- package/src/__tests__/distributed-claimer.test.ts +147 -0
- package/src/__tests__/done-log-audit.test.ts +342 -0
- package/src/__tests__/framework.test.ts +865 -0
- package/src/__tests__/goal-synthesizer.test.ts +236 -0
- package/src/__tests__/presence.test.ts +223 -0
- package/src/__tests__/protocol-agent.test.ts +254 -0
- package/src/__tests__/revenue-splitter.test.ts +114 -0
- package/src/__tests__/scenario-driven-todo.test.ts +197 -0
- package/src/__tests__/self-improve.test.ts +349 -0
- package/src/__tests__/service-lifecycle.test.ts +237 -0
- package/src/__tests__/skill-router.test.ts +121 -0
- package/src/agents/AgentManifest.ts +493 -0
- package/src/agents/AgentRegistry.ts +475 -0
- package/src/agents/AgentTypes.ts +585 -0
- package/src/agents/AgentWalletRegistry.ts +83 -0
- package/src/agents/AuthenticatedCRDT.ts +388 -0
- package/src/agents/CapabilityMatcher.ts +453 -0
- package/src/agents/CrossRealityHandoff.ts +305 -0
- package/src/agents/CulturalMemory.ts +454 -0
- package/src/agents/FederatedRegistryAdapter.ts +429 -0
- package/src/agents/NormEngine.ts +450 -0
- package/src/agents/OrchestratorAgent.ts +414 -0
- package/src/agents/SkillWorkflowEngine.ts +472 -0
- package/src/agents/TaskDelegationService.ts +551 -0
- package/src/agents/__tests__/AgentManifest.prod.test.ts +134 -0
- package/src/agents/__tests__/AgentManifest.test.ts +182 -0
- package/src/agents/__tests__/AgentModule.test.ts +864 -0
- package/src/agents/__tests__/AgentRegistry.prod.test.ts +125 -0
- package/src/agents/__tests__/AgentRegistry.test.ts +148 -0
- package/src/agents/__tests__/AgentTypes.test.ts +534 -0
- package/src/agents/__tests__/AgentWalletRegistry.test.ts +152 -0
- package/src/agents/__tests__/AuthenticatedCRDT.test.ts +558 -0
- package/src/agents/__tests__/CapabilityMatcher.prod.test.ts +117 -0
- package/src/agents/__tests__/CapabilityMatcher.test.ts +178 -0
- package/src/agents/__tests__/CrossRealityHandoff.test.ts +402 -0
- package/src/agents/__tests__/CulturalMemory.test.ts +200 -0
- package/src/agents/__tests__/FederatedRegistryAdapter.test.ts +409 -0
- package/src/agents/__tests__/NormEngine.test.ts +276 -0
- package/src/agents/__tests__/OrchestratorAgent.test.ts +182 -0
- package/src/agents/__tests__/SkillWorkflowEngine.test.ts +357 -0
- package/src/agents/__tests__/TaskDelegationService.test.ts +446 -0
- package/src/agents/index.ts +107 -0
- package/src/agents/spatial-comms/Layer1RealTime.ts +621 -0
- package/src/agents/spatial-comms/Layer2A2A.ts +661 -0
- package/src/agents/spatial-comms/Layer3MCP.ts +651 -0
- package/src/agents/spatial-comms/ProtocolTypes.ts +543 -0
- package/src/agents/spatial-comms/SpatialCommClient.ts +483 -0
- package/src/agents/spatial-comms/__tests__/performance-benchmark.test.ts +465 -0
- package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +409 -0
- package/src/agents/spatial-comms/index.ts +66 -0
- package/src/ai/AIAdapter.ts +313 -0
- package/src/ai/AICopilot.ts +331 -0
- package/src/ai/AIOutputValidator.ts +203 -0
- package/src/ai/BTNodes.ts +239 -0
- package/src/ai/BehaviorSelector.ts +135 -0
- package/src/ai/BehaviorTree.ts +153 -0
- package/src/ai/Blackboard.ts +165 -0
- package/src/ai/GenerationAnalytics.ts +461 -0
- package/src/ai/GenerationCache.ts +265 -0
- package/src/ai/GoalPlanner.ts +165 -0
- package/src/ai/HoloScriptGenerator.ts +580 -0
- package/src/ai/InfluenceMap.ts +180 -0
- package/src/ai/NavMesh.ts +168 -0
- package/src/ai/PerceptionSystem.ts +178 -0
- package/src/ai/PromptTemplates.ts +453 -0
- package/src/ai/SemanticSearchService.ts +80 -0
- package/src/ai/StateMachine.ts +196 -0
- package/src/ai/SteeringBehavior.ts +150 -0
- package/src/ai/SteeringBehaviors.ts +244 -0
- package/src/ai/TrainingDataGenerator.ts +1082 -0
- package/src/ai/UtilityAI.ts +145 -0
- package/src/ai/__tests__/AIAdapter.prod.test.ts +259 -0
- package/src/ai/__tests__/AIAdapter.test.ts +109 -0
- package/src/ai/__tests__/AICopilot.prod.test.ts +341 -0
- package/src/ai/__tests__/AICopilot.test.ts +178 -0
- package/src/ai/__tests__/AIOutputValidator.prod.test.ts +226 -0
- package/src/ai/__tests__/AIOutputValidator.test.ts +138 -0
- package/src/ai/__tests__/BTNodes.prod.test.ts +391 -0
- package/src/ai/__tests__/BTNodes.test.ts +263 -0
- package/src/ai/__tests__/BehaviorSelector.prod.test.ts +129 -0
- package/src/ai/__tests__/BehaviorSelector.test.ts +132 -0
- package/src/ai/__tests__/BehaviorTree.prod.test.ts +266 -0
- package/src/ai/__tests__/BehaviorTree.test.ts +216 -0
- package/src/ai/__tests__/Blackboard.prod.test.ts +339 -0
- package/src/ai/__tests__/Blackboard.test.ts +183 -0
- package/src/ai/__tests__/GenerationAnalytics.prod.test.ts +141 -0
- package/src/ai/__tests__/GenerationAnalytics.test.ts +165 -0
- package/src/ai/__tests__/GenerationCache.prod.test.ts +144 -0
- package/src/ai/__tests__/GenerationCache.test.ts +171 -0
- package/src/ai/__tests__/GoalPlanner.prod.test.ts +189 -0
- package/src/ai/__tests__/GoalPlanner.test.ts +137 -0
- package/src/ai/__tests__/GoalPlannerDepth.prod.test.ts +217 -0
- package/src/ai/__tests__/HoloScriptGenerator.test.ts +125 -0
- package/src/ai/__tests__/InfluenceMap.prod.test.ts +146 -0
- package/src/ai/__tests__/InfluenceMap.test.ts +149 -0
- package/src/ai/__tests__/NavMesh.prod.test.ts +141 -0
- package/src/ai/__tests__/NavMesh.test.ts +159 -0
- package/src/ai/__tests__/PerceptionSystem.prod.test.ts +135 -0
- package/src/ai/__tests__/PerceptionSystem.test.ts +250 -0
- package/src/ai/__tests__/PromptTemplates.prod.test.ts +313 -0
- package/src/ai/__tests__/PromptTemplates.test.ts +146 -0
- package/src/ai/__tests__/SemanticSearch.test.ts +37 -0
- package/src/ai/__tests__/StateMachine.prod.test.ts +162 -0
- package/src/ai/__tests__/StateMachine.test.ts +163 -0
- package/src/ai/__tests__/SteeringBehavior.prod.test.ts +251 -0
- package/src/ai/__tests__/SteeringBehavior.test.ts +135 -0
- package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +133 -0
- package/src/ai/__tests__/SteeringBehaviors.test.ts +151 -0
- package/src/ai/__tests__/TrainingDataGenerator.prod.test.ts +286 -0
- package/src/ai/__tests__/TrainingDataGenerator.test.ts +286 -0
- package/src/ai/__tests__/UtilityAI.prod.test.ts +207 -0
- package/src/ai/__tests__/UtilityAI.test.ts +155 -0
- package/src/ai/__tests__/adapters.prod.test.ts +263 -0
- package/src/ai/__tests__/adapters.test.ts +320 -0
- package/src/ai/adapters.ts +1585 -0
- package/src/ai/index.ts +130 -0
- package/src/behavior/BehaviorPresets.ts +140 -0
- package/src/behavior/BehaviorTree.ts +236 -0
- package/src/behavior/StateMachine.ts +176 -0
- package/src/behavior/StateTrait.ts +67 -0
- package/src/behavior/index.ts +8 -0
- package/src/behavior.ts +8 -0
- package/src/board/audit.ts +284 -0
- package/src/board/board-ops.ts +336 -0
- package/src/board/board-types.ts +302 -0
- package/src/board/index.ts +69 -0
- package/src/define-agent.ts +46 -0
- package/src/define-team.ts +33 -0
- package/src/delegation.ts +265 -0
- package/src/distributed-claimer.ts +228 -0
- package/src/economy/AgentBudgetEnforcer.ts +464 -0
- package/src/economy/BountyManager.ts +185 -0
- package/src/economy/CreatorRevenueAggregator.ts +460 -0
- package/src/economy/InvisibleWallet.ts +82 -0
- package/src/economy/KnowledgeMarketplace.ts +193 -0
- package/src/economy/PaymentWebhookService.ts +512 -0
- package/src/economy/RevenueSplitter.ts +156 -0
- package/src/economy/SubscriptionManager.ts +546 -0
- package/src/economy/UnifiedBudgetOptimizer.ts +635 -0
- package/src/economy/UsageMeter.ts +440 -0
- package/src/economy/_core-stubs.ts +219 -0
- package/src/economy/index.ts +100 -0
- package/src/economy/x402-facilitator.ts +1978 -0
- package/src/index.ts +348 -0
- package/src/knowledge/__tests__/knowledge-consolidator.test.ts +444 -0
- package/src/knowledge/__tests__/knowledge-store-vector.test.ts +291 -0
- package/src/knowledge/brain.ts +167 -0
- package/src/knowledge/consolidation.ts +581 -0
- package/src/knowledge/knowledge-consolidator.ts +510 -0
- package/src/knowledge/knowledge-store.ts +616 -0
- package/src/learning/MemoryConsolidator.ts +102 -0
- package/src/learning/MemoryScorer.ts +69 -0
- package/src/learning/ProceduralCompiler.ts +45 -0
- package/src/learning/SemanticClusterer.ts +66 -0
- package/src/learning/index.ts +8 -0
- package/src/llm/llm-adapter.ts +159 -0
- package/src/mesh/index.ts +309 -0
- package/src/negotiation/NegotiationProtocol.ts +694 -0
- package/src/negotiation/NegotiationTypes.ts +473 -0
- package/src/negotiation/VotingMechanisms.ts +691 -0
- package/src/negotiation/index.ts +49 -0
- package/src/protocol/goal-synthesizer.ts +317 -0
- package/src/protocol/implementations.ts +474 -0
- package/src/protocol/micro-phase-decomposer.ts +299 -0
- package/src/protocol/micro-step-decomposer.test.ts +306 -0
- package/src/protocol-agent.test.ts +353 -0
- package/src/protocol-agent.ts +670 -0
- package/src/self-improve/absorb-scanner.ts +252 -0
- package/src/self-improve/evolution-engine.ts +149 -0
- package/src/self-improve/framework-absorber.ts +214 -0
- package/src/self-improve/index.ts +50 -0
- package/src/self-improve/prompt-optimizer.ts +212 -0
- package/src/self-improve/test-generator.ts +175 -0
- package/src/skill-router.ts +186 -0
- package/src/skills/index.ts +5 -0
- package/src/skills/skill-md-bridge.ts +1699 -0
- package/src/swarm/ACOEngine.ts +261 -0
- package/src/swarm/CollectiveIntelligence.ts +383 -0
- package/src/swarm/ContributionSynthesizer.ts +481 -0
- package/src/swarm/LeaderElection.ts +393 -0
- package/src/swarm/PSOEngine.ts +206 -0
- package/src/swarm/QuorumPolicy.ts +173 -0
- package/src/swarm/SwarmCoordinator.ts +335 -0
- package/src/swarm/SwarmManager.ts +442 -0
- package/src/swarm/SwarmMembership.ts +456 -0
- package/src/swarm/VotingRound.ts +255 -0
- package/src/swarm/__tests__/ACOEngine.prod.test.ts +164 -0
- package/src/swarm/__tests__/ACOEngine.test.ts +117 -0
- package/src/swarm/__tests__/CollectiveIntelligence.prod.test.ts +296 -0
- package/src/swarm/__tests__/CollectiveIntelligence.test.ts +457 -0
- package/src/swarm/__tests__/ContributionSynthesizer.prod.test.ts +269 -0
- package/src/swarm/__tests__/ContributionSynthesizer.test.ts +254 -0
- package/src/swarm/__tests__/LeaderElection.prod.test.ts +196 -0
- package/src/swarm/__tests__/LeaderElection.test.ts +151 -0
- package/src/swarm/__tests__/PSOEngine.prod.test.ts +162 -0
- package/src/swarm/__tests__/PSOEngine.test.ts +106 -0
- package/src/swarm/__tests__/QuorumPolicy.prod.test.ts +216 -0
- package/src/swarm/__tests__/QuorumPolicy.test.ts +177 -0
- package/src/swarm/__tests__/SwarmCoordinator.prod.test.ts +186 -0
- package/src/swarm/__tests__/SwarmCoordinator.test.ts +167 -0
- package/src/swarm/__tests__/SwarmManager.prod.test.ts +308 -0
- package/src/swarm/__tests__/SwarmManager.test.ts +373 -0
- package/src/swarm/__tests__/SwarmMembership.prod.test.ts +273 -0
- package/src/swarm/__tests__/SwarmMembership.test.ts +264 -0
- package/src/swarm/__tests__/VotingRound.prod.test.ts +233 -0
- package/src/swarm/__tests__/VotingRound.test.ts +174 -0
- package/src/swarm/analytics/SwarmInspector.ts +476 -0
- package/src/swarm/analytics/SwarmMetrics.ts +449 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.prod.test.ts +366 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.test.ts +454 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.prod.test.ts +254 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.test.ts +370 -0
- package/src/swarm/analytics/index.ts +7 -0
- package/src/swarm/index.ts +69 -0
- package/src/swarm/messaging/BroadcastChannel.ts +509 -0
- package/src/swarm/messaging/GossipProtocol.ts +565 -0
- package/src/swarm/messaging/SwarmEventBus.ts +443 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +331 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.test.ts +333 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +356 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.test.ts +437 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +191 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +247 -0
- package/src/swarm/messaging/index.ts +8 -0
- package/src/swarm/spatial/FlockingBehavior.ts +462 -0
- package/src/swarm/spatial/FormationController.ts +500 -0
- package/src/swarm/spatial/Vector3.ts +170 -0
- package/src/swarm/spatial/ZoneClaiming.ts +509 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.prod.test.ts +239 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.test.ts +298 -0
- package/src/swarm/spatial/__tests__/FormationController.prod.test.ts +240 -0
- package/src/swarm/spatial/__tests__/FormationController.test.ts +297 -0
- package/src/swarm/spatial/__tests__/Vector3.prod.test.ts +283 -0
- package/src/swarm/spatial/__tests__/Vector3.test.ts +224 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.prod.test.ts +246 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.test.ts +374 -0
- package/src/swarm/spatial/index.ts +28 -0
- package/src/team.ts +1245 -0
- package/src/training/LRScheduler.ts +377 -0
- package/src/training/QualityScoringPipeline.ts +139 -0
- package/src/training/SoftDedup.ts +461 -0
- package/src/training/SparsityMonitor.ts +685 -0
- package/src/training/SparsityMonitorTypes.ts +209 -0
- package/src/training/SpatialTrainingDataGenerator.ts +1526 -0
- package/src/training/SpatialTrainingDataTypes.ts +216 -0
- package/src/training/TrainingPipelineConfig.ts +215 -0
- package/src/training/constants.ts +94 -0
- package/src/training/index.ts +138 -0
- package/src/training/schema.ts +147 -0
- package/src/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
- package/src/training/scripts/generate-spatial-dataset.ts +521 -0
- package/src/training/training/data/novel-use-cases.jsonl +153 -0
- package/src/training/training/data/spatial-reasoning-10k.jsonl +9354 -0
- package/src/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
- package/src/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
- package/src/training/trainingmonkey/index.ts +26 -0
- package/src/training/trait-mappings.ts +157 -0
- package/src/types/core-stubs.d.ts +113 -0
- package/src/types.ts +304 -0
- package/test-output.txt +0 -0
- package/test-result.json +1 -0
- package/tsc-errors.txt +4 -0
- package/tsc_output.txt +0 -0
- package/tsconfig.json +14 -0
- package/tsup-learning-esm.config.ts +12 -0
- package/tsup.config.ts +21 -0
- package/typescript-errors-2.txt +0 -0
- package/typescript-errors.txt +22 -0
- package/vitest-log-utf8.txt +268 -0
- package/vitest-log.txt +0 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { Blackboard } from '../Blackboard';
|
|
3
|
+
|
|
4
|
+
describe('Blackboard', () => {
|
|
5
|
+
let bb: Blackboard;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
bb = new Blackboard();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Get / Set / Has / Delete
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
it('set and get round-trip', () => {
|
|
16
|
+
bb.set('hp', 100);
|
|
17
|
+
expect(bb.get('hp')).toBe(100);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('get returns undefined for missing key', () => {
|
|
21
|
+
expect(bb.get('nope')).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('has checks key existence', () => {
|
|
25
|
+
bb.set('hp', 100);
|
|
26
|
+
expect(bb.has('hp')).toBe(true);
|
|
27
|
+
expect(bb.has('mp')).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('delete removes key', () => {
|
|
31
|
+
bb.set('hp', 100);
|
|
32
|
+
expect(bb.delete('hp')).toBe(true);
|
|
33
|
+
expect(bb.has('hp')).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('delete returns false for missing key', () => {
|
|
37
|
+
expect(bb.delete('nope')).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('overwrite existing key', () => {
|
|
41
|
+
bb.set('hp', 100);
|
|
42
|
+
bb.set('hp', 50);
|
|
43
|
+
expect(bb.get('hp')).toBe(50);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Scopes
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
it('set with scope groups keys', () => {
|
|
51
|
+
bb.set('agentHp', 100, 'agent');
|
|
52
|
+
bb.set('agentMp', 50, 'agent');
|
|
53
|
+
bb.set('envTemp', 25, 'env');
|
|
54
|
+
const agentData = bb.getByScope('agent');
|
|
55
|
+
expect(agentData.size).toBe(2);
|
|
56
|
+
expect(agentData.get('agentHp')).toBe(100);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('getByScope returns empty for unknown scope', () => {
|
|
60
|
+
expect(bb.getByScope('nope').size).toBe(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('clearScope removes all keys in scope', () => {
|
|
64
|
+
bb.set('a', 1, 'temp');
|
|
65
|
+
bb.set('b', 2, 'temp');
|
|
66
|
+
bb.set('c', 3, 'keep');
|
|
67
|
+
const removed = bb.clearScope('temp');
|
|
68
|
+
expect(removed).toBe(2);
|
|
69
|
+
expect(bb.has('a')).toBe(false);
|
|
70
|
+
expect(bb.has('c')).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('getScopes lists all scopes', () => {
|
|
74
|
+
bb.set('a', 1, 'x');
|
|
75
|
+
bb.set('b', 2, 'y');
|
|
76
|
+
const scopes = bb.getScopes();
|
|
77
|
+
expect(scopes).toContain('x');
|
|
78
|
+
expect(scopes).toContain('y');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Observers
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
it('observe fires callback on set', () => {
|
|
86
|
+
const cb = vi.fn();
|
|
87
|
+
bb.observe('hp', cb);
|
|
88
|
+
bb.set('hp', 100);
|
|
89
|
+
expect(cb).toHaveBeenCalledWith('hp', 100, undefined);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('observe callback receives old value on update', () => {
|
|
93
|
+
const cb = vi.fn();
|
|
94
|
+
bb.set('hp', 100);
|
|
95
|
+
bb.observe('hp', cb);
|
|
96
|
+
bb.set('hp', 50);
|
|
97
|
+
expect(cb).toHaveBeenCalledWith('hp', 50, 100);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('observeAll fires for any key change', () => {
|
|
101
|
+
const cb = vi.fn();
|
|
102
|
+
bb.observeAll(cb);
|
|
103
|
+
bb.set('a', 1);
|
|
104
|
+
bb.set('b', 2);
|
|
105
|
+
expect(cb).toHaveBeenCalledTimes(2);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Version Tracking
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
it('getVersion increments on update', () => {
|
|
113
|
+
bb.set('hp', 100);
|
|
114
|
+
expect(bb.getVersion('hp')).toBe(0);
|
|
115
|
+
bb.set('hp', 50);
|
|
116
|
+
expect(bb.getVersion('hp')).toBe(1);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('getVersion returns -1 for missing key', () => {
|
|
120
|
+
expect(bb.getVersion('nope')).toBe(-1);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// History
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
it('getHistory records changes', () => {
|
|
128
|
+
bb.set('hp', 100);
|
|
129
|
+
bb.set('hp', 50);
|
|
130
|
+
const history = bb.getHistory();
|
|
131
|
+
expect(history.length).toBe(2);
|
|
132
|
+
expect(history[0].key).toBe('hp');
|
|
133
|
+
expect(history[0].value).toBe(100);
|
|
134
|
+
expect(history[1].value).toBe(50);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Queries
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
it('getEntryCount counts total entries', () => {
|
|
142
|
+
expect(bb.getEntryCount()).toBe(0);
|
|
143
|
+
bb.set('a', 1);
|
|
144
|
+
bb.set('b', 2);
|
|
145
|
+
expect(bb.getEntryCount()).toBe(2);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('getKeys lists all keys', () => {
|
|
149
|
+
bb.set('x', 1);
|
|
150
|
+
bb.set('y', 2);
|
|
151
|
+
expect(bb.getKeys().sort()).toEqual(['x', 'y']);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Clear
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
it('clear removes all data', () => {
|
|
159
|
+
bb.set('a', 1);
|
|
160
|
+
bb.set('b', 2);
|
|
161
|
+
bb.clear();
|
|
162
|
+
expect(bb.getEntryCount()).toBe(0);
|
|
163
|
+
expect(bb.getHistory()).toHaveLength(0);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Serialization
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
it('toJSON exports key-value pairs', () => {
|
|
171
|
+
bb.set('hp', 100);
|
|
172
|
+
bb.set('name', 'hero');
|
|
173
|
+
const json = bb.toJSON();
|
|
174
|
+
expect(json.hp).toBe(100);
|
|
175
|
+
expect(json.name).toBe('hero');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('fromJSON imports key-value pairs', () => {
|
|
179
|
+
bb.fromJSON({ x: 10, y: 20 });
|
|
180
|
+
expect(bb.get('x')).toBe(10);
|
|
181
|
+
expect(bb.get('y')).toBe(20);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenerationAnalytics — Production Test Suite
|
|
3
|
+
*
|
|
4
|
+
* Covers: metric recording, aggregate computation, adapter metrics,
|
|
5
|
+
* confidence/response-time/error distributions, recommendations,
|
|
6
|
+
* time series, export, report generation, clear.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
GenerationAnalytics,
|
|
11
|
+
createAnalytics,
|
|
12
|
+
type GenerationMetrics,
|
|
13
|
+
} from '../GenerationAnalytics';
|
|
14
|
+
|
|
15
|
+
function metric(overrides: Partial<GenerationMetrics> = {}): GenerationMetrics {
|
|
16
|
+
return {
|
|
17
|
+
promptLength: overrides.promptLength ?? 100,
|
|
18
|
+
codeLength: overrides.codeLength ?? 200,
|
|
19
|
+
confidence: overrides.confidence ?? 0.8,
|
|
20
|
+
parseSuccess: overrides.parseSuccess ?? true,
|
|
21
|
+
errorCount: overrides.errorCount ?? 0,
|
|
22
|
+
wasFixed: overrides.wasFixed ?? false,
|
|
23
|
+
responseTimeMs: overrides.responseTimeMs ?? 500,
|
|
24
|
+
attemptsNeeded: overrides.attemptsNeeded ?? 1,
|
|
25
|
+
adapterName: overrides.adapterName ?? 'openai',
|
|
26
|
+
timestamp: overrides.timestamp ?? new Date(),
|
|
27
|
+
platform: overrides.platform ?? 'web',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('GenerationAnalytics — Production', () => {
|
|
32
|
+
// ─── Factory ──────────────────────────────────────────────────────
|
|
33
|
+
it('createAnalytics returns instance', () => {
|
|
34
|
+
expect(createAnalytics()).toBeInstanceOf(GenerationAnalytics);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// ─── Record + Get ─────────────────────────────────────────────────
|
|
38
|
+
it('recordMetric stores metric', () => {
|
|
39
|
+
const a = new GenerationAnalytics();
|
|
40
|
+
a.recordMetric(metric());
|
|
41
|
+
expect(a.getAllMetrics().length).toBe(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('clearMetrics empties all', () => {
|
|
45
|
+
const a = new GenerationAnalytics();
|
|
46
|
+
a.recordMetric(metric());
|
|
47
|
+
a.clearMetrics();
|
|
48
|
+
expect(a.getAllMetrics().length).toBe(0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// ─── Aggregates ───────────────────────────────────────────────────
|
|
52
|
+
it('getAggregateMetrics computes totals', () => {
|
|
53
|
+
const a = new GenerationAnalytics();
|
|
54
|
+
a.recordMetric(metric({ parseSuccess: true, confidence: 0.9, responseTimeMs: 100 }));
|
|
55
|
+
a.recordMetric(metric({ parseSuccess: false, confidence: 0.5, responseTimeMs: 300 }));
|
|
56
|
+
const agg = a.getAggregateMetrics();
|
|
57
|
+
expect(agg.totalGenerations).toBe(2);
|
|
58
|
+
expect(agg.successRate).toBe(0.5);
|
|
59
|
+
expect(agg.avgConfidence).toBeCloseTo(0.7, 1);
|
|
60
|
+
expect(agg.avgResponseTime).toBeCloseTo(200, 0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ─── Adapter Metrics ──────────────────────────────────────────────
|
|
64
|
+
it('getMetricsByAdapter returns per-adapter stats', () => {
|
|
65
|
+
const a = new GenerationAnalytics();
|
|
66
|
+
a.recordMetric(metric({ adapterName: 'openai', parseSuccess: true }));
|
|
67
|
+
a.recordMetric(metric({ adapterName: 'openai', parseSuccess: true }));
|
|
68
|
+
a.recordMetric(metric({ adapterName: 'claude', parseSuccess: false }));
|
|
69
|
+
const oai = a.getMetricsByAdapter('openai');
|
|
70
|
+
expect(oai.generationCount).toBe(2);
|
|
71
|
+
expect(oai.successRate).toBe(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('getAllAdapterMetrics covers all adapters', () => {
|
|
75
|
+
const a = new GenerationAnalytics();
|
|
76
|
+
a.recordMetric(metric({ adapterName: 'openai' }));
|
|
77
|
+
a.recordMetric(metric({ adapterName: 'claude' }));
|
|
78
|
+
const all = a.getAllAdapterMetrics();
|
|
79
|
+
expect(all.length).toBe(2);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ─── Distributions ────────────────────────────────────────────────
|
|
83
|
+
it('getConfidenceDistribution returns buckets', () => {
|
|
84
|
+
const a = new GenerationAnalytics();
|
|
85
|
+
a.recordMetric(metric({ confidence: 0.15 }));
|
|
86
|
+
a.recordMetric(metric({ confidence: 0.55 }));
|
|
87
|
+
a.recordMetric(metric({ confidence: 0.95 }));
|
|
88
|
+
const dist = a.getConfidenceDistribution();
|
|
89
|
+
expect(dist.length).toBeGreaterThan(0);
|
|
90
|
+
expect(dist.reduce((s, d) => s + d.count, 0)).toBe(3);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('getResponseTimeDistribution returns buckets', () => {
|
|
94
|
+
const a = new GenerationAnalytics();
|
|
95
|
+
a.recordMetric(metric({ responseTimeMs: 50 }));
|
|
96
|
+
a.recordMetric(metric({ responseTimeMs: 500 }));
|
|
97
|
+
a.recordMetric(metric({ responseTimeMs: 5000 }));
|
|
98
|
+
const dist = a.getResponseTimeDistribution();
|
|
99
|
+
expect(dist.length).toBeGreaterThan(0);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('getErrorPatterns categorizes errors', () => {
|
|
103
|
+
const a = new GenerationAnalytics();
|
|
104
|
+
a.recordMetric(metric({ parseSuccess: false, errorCount: 2 }));
|
|
105
|
+
a.recordMetric(metric({ parseSuccess: true, errorCount: 0 }));
|
|
106
|
+
const patterns = a.getErrorPatterns();
|
|
107
|
+
expect(patterns.length).toBeGreaterThan(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ─── Recommendations ─────────────────────────────────────────────
|
|
111
|
+
it('getRecommendations returns array of strings', () => {
|
|
112
|
+
const a = new GenerationAnalytics();
|
|
113
|
+
a.recordMetric(metric());
|
|
114
|
+
const recs = a.getRecommendations();
|
|
115
|
+
expect(Array.isArray(recs)).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ─── Export / Report ──────────────────────────────────────────────
|
|
119
|
+
it('exportMetrics returns valid JSON', () => {
|
|
120
|
+
const a = new GenerationAnalytics();
|
|
121
|
+
a.recordMetric(metric());
|
|
122
|
+
const json = a.exportMetrics();
|
|
123
|
+
expect(() => JSON.parse(json)).not.toThrow();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('generateReport returns non-empty string', () => {
|
|
127
|
+
const a = new GenerationAnalytics();
|
|
128
|
+
a.recordMetric(metric());
|
|
129
|
+
const report = a.generateReport();
|
|
130
|
+
expect(report.length).toBeGreaterThan(0);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ─── Time Series ──────────────────────────────────────────────────
|
|
134
|
+
it('getTimeSeries returns metrics windows', () => {
|
|
135
|
+
const a = new GenerationAnalytics();
|
|
136
|
+
const now = new Date();
|
|
137
|
+
a.recordMetric(metric({ timestamp: now }));
|
|
138
|
+
const ts = a.getTimeSeries(3600000);
|
|
139
|
+
expect(ts.length).toBeGreaterThanOrEqual(0); // may or may not window
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { GenerationAnalytics } from '../GenerationAnalytics';
|
|
3
|
+
import type { GenerationMetrics } from '../GenerationAnalytics';
|
|
4
|
+
|
|
5
|
+
function makeMetric(overrides: Partial<GenerationMetrics> = {}): GenerationMetrics {
|
|
6
|
+
return {
|
|
7
|
+
promptLength: 50,
|
|
8
|
+
codeLength: 200,
|
|
9
|
+
confidence: 0.9,
|
|
10
|
+
parseSuccess: true,
|
|
11
|
+
errorCount: 0,
|
|
12
|
+
wasFixed: false,
|
|
13
|
+
responseTimeMs: 500,
|
|
14
|
+
attemptsNeeded: 1,
|
|
15
|
+
adapterName: 'gpt-4',
|
|
16
|
+
timestamp: new Date(),
|
|
17
|
+
...overrides,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('GenerationAnalytics', () => {
|
|
22
|
+
let ga: GenerationAnalytics;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
ga = new GenerationAnalytics();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Record / Get
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
it('recordMetric stores a metric', () => {
|
|
33
|
+
ga.recordMetric(makeMetric());
|
|
34
|
+
expect(ga.getAllMetrics()).toHaveLength(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('getAllMetrics returns all recorded metrics', () => {
|
|
38
|
+
ga.recordMetric(makeMetric());
|
|
39
|
+
ga.recordMetric(makeMetric({ confidence: 0.5 }));
|
|
40
|
+
expect(ga.getAllMetrics()).toHaveLength(2);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('clearMetrics removes all metrics', () => {
|
|
44
|
+
ga.recordMetric(makeMetric());
|
|
45
|
+
ga.clearMetrics();
|
|
46
|
+
expect(ga.getAllMetrics()).toHaveLength(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Aggregate Metrics
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
it('getAggregateMetrics calculates totalGenerations', () => {
|
|
54
|
+
ga.recordMetric(makeMetric());
|
|
55
|
+
ga.recordMetric(makeMetric());
|
|
56
|
+
const agg = ga.getAggregateMetrics();
|
|
57
|
+
expect(agg.totalGenerations).toBe(2);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('getAggregateMetrics computes success rate', () => {
|
|
61
|
+
ga.recordMetric(makeMetric({ parseSuccess: true }));
|
|
62
|
+
ga.recordMetric(makeMetric({ parseSuccess: false }));
|
|
63
|
+
const agg = ga.getAggregateMetrics();
|
|
64
|
+
expect(agg.successRate).toBeCloseTo(0.5);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('getAggregateMetrics computes average confidence', () => {
|
|
68
|
+
ga.recordMetric(makeMetric({ confidence: 0.8 }));
|
|
69
|
+
ga.recordMetric(makeMetric({ confidence: 0.6 }));
|
|
70
|
+
const agg = ga.getAggregateMetrics();
|
|
71
|
+
expect(agg.avgConfidence).toBeCloseTo(0.7);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('getAggregateMetrics computes average response time', () => {
|
|
75
|
+
ga.recordMetric(makeMetric({ responseTimeMs: 400 }));
|
|
76
|
+
ga.recordMetric(makeMetric({ responseTimeMs: 600 }));
|
|
77
|
+
const agg = ga.getAggregateMetrics();
|
|
78
|
+
expect(agg.avgResponseTime).toBeCloseTo(500);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Adapter Metrics
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
it('getMetricsByAdapter returns per-adapter stats', () => {
|
|
86
|
+
ga.recordMetric(makeMetric({ adapterName: 'gpt-4', confidence: 0.9 }));
|
|
87
|
+
ga.recordMetric(makeMetric({ adapterName: 'gpt-4', confidence: 0.8 }));
|
|
88
|
+
ga.recordMetric(makeMetric({ adapterName: 'claude', confidence: 0.7 }));
|
|
89
|
+
const gpt = ga.getMetricsByAdapter('gpt-4');
|
|
90
|
+
expect(gpt.generationCount).toBe(2);
|
|
91
|
+
expect(gpt.avgConfidence).toBeCloseTo(0.85);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('getAllAdapterMetrics lists all adapters', () => {
|
|
95
|
+
ga.recordMetric(makeMetric({ adapterName: 'a' }));
|
|
96
|
+
ga.recordMetric(makeMetric({ adapterName: 'b' }));
|
|
97
|
+
const all = ga.getAllAdapterMetrics();
|
|
98
|
+
expect(all.length).toBe(2);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Confidence Distribution
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
it('getConfidenceDistribution returns buckets', () => {
|
|
106
|
+
ga.recordMetric(makeMetric({ confidence: 0.15 }));
|
|
107
|
+
ga.recordMetric(makeMetric({ confidence: 0.85 }));
|
|
108
|
+
const dist = ga.getConfidenceDistribution();
|
|
109
|
+
expect(dist.length).toBeGreaterThan(0);
|
|
110
|
+
// Should have at least entries in low and high buckets
|
|
111
|
+
const total = dist.reduce((sum, d) => sum + d.count, 0);
|
|
112
|
+
expect(total).toBe(2);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Error Patterns
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
it('getErrorPatterns identifies error types', () => {
|
|
120
|
+
ga.recordMetric(makeMetric({ parseSuccess: false, errorCount: 2 }));
|
|
121
|
+
ga.recordMetric(makeMetric({ parseSuccess: true, errorCount: 0 }));
|
|
122
|
+
const patterns = ga.getErrorPatterns();
|
|
123
|
+
expect(patterns).toBeDefined();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Recommendations
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
it('getRecommendations returns suggestions array', () => {
|
|
131
|
+
ga.recordMetric(makeMetric({ confidence: 0.3, parseSuccess: false }));
|
|
132
|
+
ga.recordMetric(makeMetric({ confidence: 0.2, parseSuccess: false }));
|
|
133
|
+
const recs = ga.getRecommendations();
|
|
134
|
+
expect(Array.isArray(recs)).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Export / Report
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
it('exportMetrics returns JSON string', () => {
|
|
142
|
+
ga.recordMetric(makeMetric());
|
|
143
|
+
const json = ga.exportMetrics();
|
|
144
|
+
const parsed = JSON.parse(json);
|
|
145
|
+
expect(parsed).toBeDefined();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('generateReport returns readable string', () => {
|
|
149
|
+
ga.recordMetric(makeMetric());
|
|
150
|
+
const report = ga.generateReport();
|
|
151
|
+
expect(typeof report).toBe('string');
|
|
152
|
+
expect(report.length).toBeGreaterThan(0);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Response Time Distribution
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
it('getResponseTimeDistribution returns buckets', () => {
|
|
160
|
+
ga.recordMetric(makeMetric({ responseTimeMs: 100 }));
|
|
161
|
+
ga.recordMetric(makeMetric({ responseTimeMs: 5000 }));
|
|
162
|
+
const dist = ga.getResponseTimeDistribution();
|
|
163
|
+
expect(dist.length).toBeGreaterThan(0);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenerationCache (LRU) — Production Test Suite
|
|
3
|
+
*
|
|
4
|
+
* Covers: set/get, key generation, TTL expiry, LRU eviction,
|
|
5
|
+
* statistics, serialization, entries, remove, getSize, clear.
|
|
6
|
+
*
|
|
7
|
+
* Note: GenerationCache uses node's crypto module for hashing,
|
|
8
|
+
* which Vitest supports natively.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
11
|
+
import { GenerationCache } from '../GenerationCache';
|
|
12
|
+
|
|
13
|
+
describe('GenerationCache — Production', () => {
|
|
14
|
+
// Helper function to mock Date.now with offset or counter
|
|
15
|
+
const mockDateNow = (offsetMs: number) => {
|
|
16
|
+
const originalNow = Date.now;
|
|
17
|
+
Date.now = vi.fn(() => originalNow() + offsetMs);
|
|
18
|
+
return () => {
|
|
19
|
+
Date.now = originalNow;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const mockDateNowIncrementing = (startOffset: number = 0) => {
|
|
24
|
+
const originalNow = Date.now;
|
|
25
|
+
let counter = startOffset;
|
|
26
|
+
Date.now = vi.fn(() => originalNow() + counter++);
|
|
27
|
+
return () => {
|
|
28
|
+
Date.now = originalNow;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// ─── Basic Get/Set ────────────────────────────────────────────────
|
|
33
|
+
it('set + get returns cached entry', () => {
|
|
34
|
+
const cache = new GenerationCache();
|
|
35
|
+
cache.set('prompt1', 'code1', 0.9, 'openai');
|
|
36
|
+
const entry = cache.get('prompt1', 'openai');
|
|
37
|
+
expect(entry).not.toBeNull();
|
|
38
|
+
expect(entry!.code).toBe('code1');
|
|
39
|
+
expect(entry!.confidence).toBe(0.9);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('get returns null on miss', () => {
|
|
43
|
+
const cache = new GenerationCache();
|
|
44
|
+
expect(cache.get('unknown', 'openai')).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('different adapters have different keys', () => {
|
|
48
|
+
const cache = new GenerationCache();
|
|
49
|
+
cache.set('prompt', 'code-oai', 0.9, 'openai');
|
|
50
|
+
cache.set('prompt', 'code-cl', 0.8, 'claude');
|
|
51
|
+
expect(cache.get('prompt', 'openai')!.code).toBe('code-oai');
|
|
52
|
+
expect(cache.get('prompt', 'claude')!.code).toBe('code-cl');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// ─── Hit Tracking ─────────────────────────────────────────────────
|
|
56
|
+
it('get increments hit count', () => {
|
|
57
|
+
const cache = new GenerationCache();
|
|
58
|
+
cache.set('p', 'c', 0.9, 'a');
|
|
59
|
+
cache.get('p', 'a');
|
|
60
|
+
cache.get('p', 'a');
|
|
61
|
+
const entry = cache.get('p', 'a');
|
|
62
|
+
expect(entry!.hits).toBe(3);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ─── TTL Expiry ───────────────────────────────────────────────────
|
|
66
|
+
it('expired entries are not returned', () => {
|
|
67
|
+
const cache = new GenerationCache({ ttlMs: 100 });
|
|
68
|
+
cache.set('p', 'c', 0.9, 'a');
|
|
69
|
+
|
|
70
|
+
// Fast-forward time
|
|
71
|
+
const restoreDateNow = mockDateNow(200);
|
|
72
|
+
expect(cache.get('p', 'a')).toBeNull();
|
|
73
|
+
restoreDateNow();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ─── LRU Eviction ─────────────────────────────────────────────────
|
|
77
|
+
it('evicts oldest when maxSize reached', () => {
|
|
78
|
+
const cache = new GenerationCache({ maxSize: 2 });
|
|
79
|
+
cache.set('first', 'c1', 0.9, 'a');
|
|
80
|
+
|
|
81
|
+
// Ensure "second" and "third" are newer with incrementing timestamps
|
|
82
|
+
const restoreDateNow = mockDateNowIncrementing(1);
|
|
83
|
+
|
|
84
|
+
cache.set('second', 'c2', 0.9, 'a');
|
|
85
|
+
cache.set('third', 'c3', 0.9, 'a'); // should evict 'first'
|
|
86
|
+
|
|
87
|
+
restoreDateNow();
|
|
88
|
+
|
|
89
|
+
expect(cache.get('first', 'a')).toBeNull();
|
|
90
|
+
expect(cache.get('third', 'a')).not.toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// ─── Statistics ───────────────────────────────────────────────────
|
|
94
|
+
it('getStats tracks hits and misses', () => {
|
|
95
|
+
const cache = new GenerationCache();
|
|
96
|
+
cache.set('p', 'c', 0.9, 'a');
|
|
97
|
+
cache.get('p', 'a'); // hit
|
|
98
|
+
cache.get('x', 'a'); // miss
|
|
99
|
+
const stats = cache.getStats();
|
|
100
|
+
expect(stats.totalHits).toBe(1);
|
|
101
|
+
expect(stats.totalMisses).toBe(1);
|
|
102
|
+
expect(stats.hitRate).toBeCloseTo(0.5);
|
|
103
|
+
expect(stats.entriesCount).toBe(1);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ─── Serialization ───────────────────────────────────────────────
|
|
107
|
+
it('serialize produces valid JSON', () => {
|
|
108
|
+
const cache = new GenerationCache();
|
|
109
|
+
cache.set('p', 'c', 0.9, 'a');
|
|
110
|
+
const json = cache.serialize();
|
|
111
|
+
expect(() => JSON.parse(json)).not.toThrow();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ─── Entries / Remove / Size ──────────────────────────────────────
|
|
115
|
+
it('getEntries returns all cached entries', () => {
|
|
116
|
+
const cache = new GenerationCache();
|
|
117
|
+
cache.set('a', 'ca', 0.9, 'openai');
|
|
118
|
+
cache.set('b', 'cb', 0.8, 'openai');
|
|
119
|
+
expect(cache.getEntries().length).toBe(2);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('remove deletes specific entry', () => {
|
|
123
|
+
const cache = new GenerationCache();
|
|
124
|
+
cache.set('p', 'c', 0.9, 'a');
|
|
125
|
+
expect(cache.remove('p', 'a')).toBe(true);
|
|
126
|
+
expect(cache.get('p', 'a')).toBeNull();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('getSize returns approximate byte size', () => {
|
|
130
|
+
const cache = new GenerationCache();
|
|
131
|
+
cache.set('p', 'c', 0.9, 'a');
|
|
132
|
+
expect(cache.getSize()).toBeGreaterThan(0);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// ─── Clear ────────────────────────────────────────────────────────
|
|
136
|
+
it('clear empties cache and resets stats', () => {
|
|
137
|
+
const cache = new GenerationCache();
|
|
138
|
+
cache.set('p', 'c', 0.9, 'a');
|
|
139
|
+
cache.get('p', 'a');
|
|
140
|
+
cache.clear();
|
|
141
|
+
expect(cache.getEntries().length).toBe(0);
|
|
142
|
+
expect(cache.getStats().totalHits).toBe(0);
|
|
143
|
+
});
|
|
144
|
+
});
|