@holoscript/framework 6.0.3 → 6.0.4

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.
Files changed (160) hide show
  1. package/CHANGELOG.md +1 -2
  2. package/ROADMAP.md +68 -66
  3. package/dist/{InvisibleWallet-BB6tFvRA.d.cts → InvisibleWallet-EFiuaLn3.d.cts} +1 -1
  4. package/dist/{OrchestratorAgent-BvWgf9uw.d.cts → OrchestratorAgent-CrLDGNL6.d.cts} +1 -1
  5. package/dist/agents/index.cjs +11 -10
  6. package/dist/agents/index.d.cts +4 -16
  7. package/dist/ai/index.cjs +2 -2
  8. package/dist/behavior.cjs +10 -0
  9. package/dist/economy/index.cjs +4 -4
  10. package/dist/economy/index.d.cts +2 -2
  11. package/dist/index.cjs +33 -11
  12. package/dist/index.d.cts +3 -3
  13. package/dist/swarm/index.cjs +3 -0
  14. package/package.json +14 -9
  15. package/src/__tests__/bounty-marketplace.test.ts +53 -21
  16. package/src/__tests__/delegation.test.ts +1 -4
  17. package/src/__tests__/done-log-audit.test.ts +38 -46
  18. package/src/__tests__/framework.test.ts +172 -53
  19. package/src/__tests__/goal-synthesizer.test.ts +9 -6
  20. package/src/__tests__/presence.test.ts +1 -1
  21. package/src/__tests__/protocol-agent.test.ts +12 -11
  22. package/src/__tests__/revenue-splitter.test.ts +22 -15
  23. package/src/__tests__/scenario-driven-todo.test.ts +55 -35
  24. package/src/__tests__/self-improve.test.ts +28 -9
  25. package/src/__tests__/service-lifecycle.test.ts +9 -3
  26. package/src/__tests__/skill-router.test.ts +3 -3
  27. package/src/agents/CulturalMemory.ts +6 -6
  28. package/src/agents/DelegationTraceHooks.ts +560 -0
  29. package/src/agents/FederatedRegistryAdapter.ts +1 -1
  30. package/src/agents/NormEngine.ts +3 -8
  31. package/src/agents/OrchestratorAgent.ts +1 -1
  32. package/src/agents/TaskDelegationService.ts +5 -9
  33. package/src/agents/__tests__/AgentWalletRegistry.test.ts +5 -4
  34. package/src/agents/__tests__/CrossRealityHandoff.test.ts +9 -3
  35. package/src/agents/__tests__/DelegationTraceHooks.test.ts +390 -0
  36. package/src/agents/__tests__/TaskDelegationService.test.ts +4 -2
  37. package/src/agents/spatial-comms/Layer1RealTime.ts +36 -19
  38. package/src/agents/spatial-comms/Layer2A2A.ts +1 -3
  39. package/src/agents/spatial-comms/Layer3MCP.ts +13 -4
  40. package/src/agents/spatial-comms/ProtocolTypes.ts +5 -2
  41. package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +2 -2
  42. package/src/ai/HoloScriptGenerator.ts +2 -2
  43. package/src/ai/__tests__/PerceptionSystem.prod.test.ts +1 -1
  44. package/src/ai/__tests__/PerceptionSystem.test.ts +14 -14
  45. package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +1 -1
  46. package/src/ai/index.ts +5 -1
  47. package/src/board/audit.ts +17 -6
  48. package/src/board/board-ops.ts +45 -15
  49. package/src/board/board-types.ts +94 -20
  50. package/src/delegation.ts +5 -3
  51. package/src/distributed-claimer.ts +13 -2
  52. package/src/economy/BountyManager.ts +40 -18
  53. package/src/economy/KnowledgeMarketplace.ts +27 -8
  54. package/src/economy/PaymentWebhookService.ts +0 -1
  55. package/src/economy/RevenueSplitter.ts +2 -4
  56. package/src/economy/UnifiedBudgetOptimizer.ts +8 -9
  57. package/src/economy/_core-stubs.ts +1 -1
  58. package/src/economy/x402-facilitator.ts +17 -8
  59. package/src/index.ts +16 -12
  60. package/src/knowledge/__tests__/knowledge-consolidator.test.ts +138 -89
  61. package/src/knowledge/__tests__/knowledge-store-vector.test.ts +59 -16
  62. package/src/knowledge/brain.ts +7 -7
  63. package/src/knowledge/consolidation.ts +16 -16
  64. package/src/knowledge/knowledge-consolidator.ts +60 -30
  65. package/src/knowledge/knowledge-store.ts +83 -45
  66. package/src/learning/ProceduralCompiler.ts +6 -1
  67. package/src/learning/learning/MemoryConsolidator.ts +102 -0
  68. package/src/learning/learning/MemoryScorer.ts +69 -0
  69. package/src/learning/learning/ProceduralCompiler.ts +45 -0
  70. package/src/learning/learning/SemanticClusterer.ts +66 -0
  71. package/src/llm/llm-adapter.ts +24 -10
  72. package/src/mesh/index.ts +37 -17
  73. package/src/protocol/goal-synthesizer.ts +24 -34
  74. package/src/protocol/implementations.ts +91 -22
  75. package/src/protocol/micro-phase-decomposer.ts +25 -17
  76. package/src/protocol/micro-step-decomposer.test.ts +104 -39
  77. package/src/protocol-agent.test.ts +17 -7
  78. package/src/protocol-agent.ts +45 -42
  79. package/src/self-improve/absorb-scanner.ts +9 -6
  80. package/src/self-improve/evolution-engine.ts +36 -18
  81. package/src/self-improve/framework-absorber.ts +21 -16
  82. package/src/self-improve/index.ts +2 -10
  83. package/src/self-improve/prompt-optimizer.ts +31 -19
  84. package/src/self-improve/test-generator.ts +16 -12
  85. package/src/skill-router.ts +7 -6
  86. package/src/swarm/messaging/GossipProtocol.ts +1 -1
  87. package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +31 -9
  88. package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +21 -7
  89. package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +24 -8
  90. package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +6 -2
  91. package/src/team.ts +277 -122
  92. package/src/training/scripts/generate-spatial-dataset.ts +1 -1
  93. package/src/training/training/LRScheduler.ts +377 -0
  94. package/src/training/training/QualityScoringPipeline.ts +139 -0
  95. package/src/training/training/SoftDedup.ts +461 -0
  96. package/src/training/training/SparsityMonitor.ts +685 -0
  97. package/src/training/training/SparsityMonitorTypes.ts +209 -0
  98. package/src/training/training/SpatialTrainingDataGenerator.ts +1526 -0
  99. package/src/training/training/SpatialTrainingDataTypes.ts +216 -0
  100. package/src/training/training/TrainingPipelineConfig.ts +215 -0
  101. package/src/training/training/__tests__/CorpusValidation.test.ts +87 -0
  102. package/src/training/training/__tests__/LRScheduler.test.ts +592 -0
  103. package/src/training/training/__tests__/SoftDedup.test.ts +415 -0
  104. package/src/training/training/__tests__/SparsityMonitor.test.ts +1623 -0
  105. package/src/training/training/__tests__/SpatialCorpusValidation.test.ts +72 -0
  106. package/src/training/training/__tests__/SpatialTrainingDataGenerator.test.ts +1244 -0
  107. package/src/training/training/__tests__/TrainingMonkeyIntegration.test.ts +897 -0
  108. package/src/training/training/__tests__/TrainingPipelineConfig.test.ts +202 -0
  109. package/src/training/training/__tests__/schema.test.ts +72 -0
  110. package/src/training/training/__tests__/training-constants.test.ts +106 -0
  111. package/src/training/training/__tests__/trait-mappings.test.ts +81 -0
  112. package/src/training/training/constants.ts +94 -0
  113. package/src/training/training/index.ts +17 -0
  114. package/src/training/training/schema.ts +147 -0
  115. package/src/training/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
  116. package/src/training/training/scripts/generate-spatial-dataset.ts +521 -0
  117. package/src/training/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
  118. package/src/training/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
  119. package/src/training/training/trainingmonkey/index.ts +26 -0
  120. package/src/training/training/trait-mappings.ts +157 -0
  121. package/src/types.ts +2 -7
  122. package/ALL-test-results.json +0 -1
  123. package/LICENSE +0 -21
  124. package/dist/AgentManifest-CB4xM-Ma.d.ts +0 -704
  125. package/dist/BehaviorTree-BrBFECv5.d.ts +0 -103
  126. package/dist/InvisibleWallet-rtRrBOA8.d.ts +0 -1732
  127. package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +0 -798
  128. package/dist/agents/index.d.ts +0 -1788
  129. package/dist/agents/index.js +0 -4695
  130. package/dist/ai/index.d.ts +0 -1753
  131. package/dist/ai/index.js +0 -5244
  132. package/dist/behavior.d.ts +0 -130
  133. package/dist/behavior.js +0 -407
  134. package/dist/economy/index.d.ts +0 -747
  135. package/dist/economy/index.js +0 -3617
  136. package/dist/implementations-D9T3un9D.d.ts +0 -236
  137. package/dist/index.d.ts +0 -1729
  138. package/dist/index.js +0 -24277
  139. package/dist/learning/index.d.ts +0 -104
  140. package/dist/learning/index.js +0 -189
  141. package/dist/negotiation/index.d.ts +0 -610
  142. package/dist/negotiation/index.js +0 -931
  143. package/dist/skills/index.d.ts +0 -289
  144. package/dist/skills/index.js +0 -1079
  145. package/dist/swarm/index.d.ts +0 -2433
  146. package/dist/swarm/index.js +0 -5221
  147. package/dist/training/index.d.ts +0 -1734
  148. package/dist/training/index.js +0 -2687
  149. package/extract-failures.js +0 -10
  150. package/src/training/training/data/novel-use-cases.jsonl +0 -153
  151. package/src/training/training/data/spatial-reasoning-10k.jsonl +0 -9354
  152. package/src/types/core-stubs.d.ts +0 -113
  153. package/test-output.txt +0 -0
  154. package/test-result.json +0 -1
  155. package/tsc-errors.txt +0 -4
  156. package/tsc_output.txt +0 -0
  157. package/typescript-errors-2.txt +0 -0
  158. package/typescript-errors.txt +0 -22
  159. package/vitest-log-utf8.txt +0 -268
  160. package/vitest-log.txt +0 -0
@@ -1,2687 +0,0 @@
1
- // src/training/SpatialTrainingDataGenerator.ts
2
- var SeededRandom = class {
3
- state;
4
- constructor(seed) {
5
- this.state = seed;
6
- }
7
- /** Returns a float in [0, 1) */
8
- next() {
9
- this.state |= 0;
10
- this.state = this.state + 1831565813 | 0;
11
- let t = Math.imul(this.state ^ this.state >>> 15, 1 | this.state);
12
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
13
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
14
- }
15
- /** Returns an integer in [min, max] inclusive */
16
- int(min, max) {
17
- return Math.floor(this.next() * (max - min + 1)) + min;
18
- }
19
- /** Returns a float in [min, max) */
20
- float(min, max) {
21
- return this.next() * (max - min) + min;
22
- }
23
- /** Picks a random element from an array */
24
- pick(array) {
25
- return array[this.int(0, array.length - 1)];
26
- }
27
- /** Shuffles an array in place */
28
- shuffle(array) {
29
- const result = [...array];
30
- for (let i = result.length - 1; i > 0; i--) {
31
- const j = this.int(0, i);
32
- [result[i], result[j]] = [result[j], result[i]];
33
- }
34
- return result;
35
- }
36
- };
37
- var ADJACENT_QUESTION_TEMPLATES = [
38
- {
39
- question: (src, tgt, _d, maxDist) => `Is "${src}" within ${maxDist}m of "${tgt}" in this scene?`,
40
- positiveAnswer: (src, tgt, dist, maxDist) => `Yes. "${src}" is ${dist.toFixed(1)}m from "${tgt}", which is within the ${maxDist}m adjacency constraint.`,
41
- negativeAnswer: (src, tgt, dist, maxDist) => `No. "${src}" is ${dist.toFixed(1)}m from "${tgt}", which exceeds the ${maxDist}m adjacency constraint.`
42
- },
43
- {
44
- question: (src, tgt) => `Are "${src}" and "${tgt}" adjacent to each other?`,
45
- positiveAnswer: (src, tgt, dist) => `Yes, "${src}" and "${tgt}" are adjacent. They are ${dist.toFixed(1)}m apart, satisfying the adjacency constraint.`,
46
- negativeAnswer: (src, tgt, dist, maxDist) => `No, "${src}" and "${tgt}" are not adjacent. At ${dist.toFixed(1)}m apart, they exceed the ${maxDist}m maximum distance.`
47
- },
48
- {
49
- question: (src, tgt, _d, maxDist) => `Does the spatial_adjacent constraint between "${src}" and "${tgt}" (maxDistance: ${maxDist}m) pass?`,
50
- positiveAnswer: (src, tgt, dist, maxDist) => `The constraint passes. The distance between "${src}" and "${tgt}" is ${dist.toFixed(1)}m, within the ${maxDist}m limit.`,
51
- negativeAnswer: (src, tgt, dist, maxDist) => `The constraint fails. The distance between "${src}" and "${tgt}" is ${dist.toFixed(1)}m, exceeding the ${maxDist}m limit.`
52
- },
53
- {
54
- question: (src, tgt) => `What is the spatial relationship between "${src}" and "${tgt}"? Are they close enough to interact?`,
55
- positiveAnswer: (src, tgt, dist) => `"${src}" and "${tgt}" are close enough to interact. They are ${dist.toFixed(1)}m apart, within the adjacency threshold.`,
56
- negativeAnswer: (src, tgt, dist) => `"${src}" and "${tgt}" are too far apart to interact. At ${dist.toFixed(1)}m apart, they fail the adjacency check.`
57
- },
58
- {
59
- question: (src, tgt, _d, maxDist) => `Can "${src}" reach "${tgt}" given the ${maxDist}m adjacency requirement?`,
60
- positiveAnswer: (src, tgt, dist) => `Yes, "${src}" can reach "${tgt}". The current distance of ${dist.toFixed(1)}m satisfies the adjacency requirement.`,
61
- negativeAnswer: (src, tgt, dist, maxDist) => `No, "${src}" cannot reach "${tgt}". The ${dist.toFixed(1)}m distance exceeds the ${maxDist}m requirement.`
62
- },
63
- {
64
- question: (src, tgt) => `In this HoloScript composition, evaluate whether "${src}" satisfies its adjacency constraint with "${tgt}".`,
65
- positiveAnswer: (src, tgt, dist, maxDist) => `The adjacency constraint is satisfied. "${src}" is ${dist.toFixed(1)}m from "${tgt}" (max allowed: ${maxDist}m).`,
66
- negativeAnswer: (src, tgt, dist, maxDist) => `The adjacency constraint is violated. "${src}" is ${dist.toFixed(1)}m from "${tgt}" but must be within ${maxDist}m.`
67
- },
68
- {
69
- question: (_src, _tgt, _d, maxDist) => `Which objects in this scene are within ${maxDist}m of each other?`,
70
- positiveAnswer: (src, tgt, dist) => `"${src}" and "${tgt}" are within range at ${dist.toFixed(1)}m apart.`,
71
- negativeAnswer: (src, tgt, dist) => `"${src}" and "${tgt}" are NOT within range. They are ${dist.toFixed(1)}m apart.`
72
- },
73
- {
74
- question: (src, tgt) => `If I move "${src}" to its current position, will it still be adjacent to "${tgt}"?`,
75
- positiveAnswer: (_src, _tgt, dist, maxDist) => `Yes, at its current position the distance is ${dist.toFixed(1)}m, which maintains adjacency (max: ${maxDist}m).`,
76
- negativeAnswer: (_src, _tgt, dist, maxDist) => `No, at its current position the distance is ${dist.toFixed(1)}m, which breaks the ${maxDist}m adjacency constraint.`
77
- },
78
- {
79
- question: (src, tgt, _d, maxDist) => `How far is "${src}" from "${tgt}"? Is it within the ${maxDist}m threshold?`,
80
- positiveAnswer: (src, tgt, dist, maxDist) => `"${src}" is ${dist.toFixed(1)}m from "${tgt}". This is within the ${maxDist}m threshold, so the adjacency holds.`,
81
- negativeAnswer: (src, tgt, dist, maxDist) => `"${src}" is ${dist.toFixed(1)}m from "${tgt}". This exceeds the ${maxDist}m threshold, so adjacency is violated.`
82
- },
83
- {
84
- question: (src, tgt) => `Analyze the spatial_adjacent constraint: is "${src}" properly positioned relative to "${tgt}"?`,
85
- positiveAnswer: (src, tgt, dist, maxDist) => `"${src}" is properly positioned at ${dist.toFixed(1)}m from "${tgt}", satisfying the ${maxDist}m adjacency constraint.`,
86
- negativeAnswer: (src, tgt, dist, maxDist) => `"${src}" is improperly positioned at ${dist.toFixed(1)}m from "${tgt}". It should be within ${maxDist}m.`
87
- },
88
- {
89
- question: (src, tgt) => `Does the scene layout satisfy the proximity requirement between "${src}" and "${tgt}"?`,
90
- positiveAnswer: (src, tgt, dist) => `Yes, the proximity requirement is satisfied. "${src}" and "${tgt}" are ${dist.toFixed(1)}m apart.`,
91
- negativeAnswer: (src, tgt, dist, maxDist) => `No, the proximity requirement is not satisfied. "${src}" is ${dist.toFixed(1)}m from "${tgt}" (max: ${maxDist}m).`
92
- },
93
- {
94
- question: (src, tgt, _d, maxDist) => `Given the HoloScript scene below, would placing "${src}" at its position violate the ${maxDist}m adjacency with "${tgt}"?`,
95
- positiveAnswer: (_src, _tgt, dist, maxDist) => `No violation. The position results in a ${dist.toFixed(1)}m distance, within the ${maxDist}m limit.`,
96
- negativeAnswer: (_src, _tgt, dist, maxDist) => `Violation detected. The position results in a ${dist.toFixed(1)}m distance, exceeding the ${maxDist}m limit.`
97
- }
98
- ];
99
- var CONTAINS_QUESTION_TEMPLATES = [
100
- {
101
- question: (container, contained) => `Is "${contained}" inside "${container}"?`,
102
- positiveAnswer: (container, contained) => `Yes, "${contained}" is fully contained within "${container}"'s bounds.`,
103
- negativeAnswer: (container, contained) => `No, "${contained}" is outside "${container}"'s bounds.`
104
- },
105
- {
106
- question: (container, contained) => `Does "${container}" contain "${contained}" according to the spatial_contains constraint?`,
107
- positiveAnswer: (container, contained) => `Yes, the spatial_contains constraint is satisfied. "${contained}" is within "${container}".`,
108
- negativeAnswer: (container, contained) => `No, the spatial_contains constraint is violated. "${contained}" extends beyond "${container}"'s bounds.`
109
- },
110
- {
111
- question: (container, contained) => `Verify whether "${contained}" fits entirely within "${container}"'s bounding volume.`,
112
- positiveAnswer: (container, contained) => `Verified: "${contained}" fits entirely within "${container}"'s bounding volume.`,
113
- negativeAnswer: (container, contained) => `Verification failed: "${contained}" does not fit within "${container}"'s bounding volume.`
114
- },
115
- {
116
- question: (container) => `Which objects are inside "${container}" in this scene?`,
117
- positiveAnswer: (container, contained) => `"${contained}" is inside "${container}", satisfying the containment constraint.`,
118
- negativeAnswer: (container, contained) => `"${contained}" is NOT inside "${container}". It has moved outside the container bounds.`
119
- },
120
- {
121
- question: (container, contained) => `If "${container}" has bounds from its declared size, is "${contained}" at its current position within those bounds?`,
122
- positiveAnswer: (_container, contained) => `Yes, "${contained}" at its current position falls within the container's declared bounds.`,
123
- negativeAnswer: (_container, contained) => `No, "${contained}" at its current position falls outside the container's declared bounds.`
124
- },
125
- {
126
- question: (container, contained) => `Evaluate the containment relationship: does "${container}" enclose "${contained}"?`,
127
- positiveAnswer: (container, contained) => `"${container}" successfully encloses "${contained}". The containment constraint is satisfied.`,
128
- negativeAnswer: (container, contained) => `"${container}" does not enclose "${contained}". The containment constraint is violated.`
129
- },
130
- {
131
- question: (container, contained) => `In this HoloScript composition, check if "${contained}" remains within the zone defined by "${container}".`,
132
- positiveAnswer: (container, contained) => `"${contained}" remains within the zone defined by "${container}".`,
133
- negativeAnswer: (container, contained) => `"${contained}" has left the zone defined by "${container}".`
134
- },
135
- {
136
- question: (container, contained) => `Can "${contained}" exist at its current position without violating the containment constraint with "${container}"?`,
137
- positiveAnswer: (_container, contained) => `Yes, "${contained}" can exist at its current position. It is within the container bounds.`,
138
- negativeAnswer: (_container, contained) => `No, "${contained}" at its current position violates the containment constraint. It is outside the bounds.`
139
- },
140
- {
141
- question: (container, contained) => `Does the bounding box of "${container}" fully enclose the position of "${contained}"?`,
142
- positiveAnswer: (container, contained) => `Yes, "${container}"'s bounding box fully encloses "${contained}"'s position.`,
143
- negativeAnswer: (container, contained) => `No, "${container}"'s bounding box does not enclose "${contained}"'s position.`
144
- },
145
- {
146
- question: (container, contained) => `Analyze the spatial hierarchy: is "${contained}" a valid child within "${container}"'s spatial region?`,
147
- positiveAnswer: (container, contained) => `"${contained}" is a valid child within "${container}"'s spatial region. Containment is satisfied.`,
148
- negativeAnswer: (container, contained) => `"${contained}" is NOT a valid child within "${container}"'s spatial region. It extends outside the boundaries.`
149
- },
150
- {
151
- question: (container, contained) => `Given the margin constraint, is "${contained}" properly contained within "${container}"?`,
152
- positiveAnswer: (container, contained) => `With the margin applied, "${contained}" is properly contained within "${container}".`,
153
- negativeAnswer: (container, contained) => `With the margin applied, "${contained}" is NOT properly contained within "${container}". It is too close to or beyond the boundary.`
154
- },
155
- {
156
- question: (container, contained) => `Would moving "${contained}" to its declared position cause it to exit "${container}"?`,
157
- positiveAnswer: (container, contained) => `No, "${contained}" at its declared position remains inside "${container}".`,
158
- negativeAnswer: (container, contained) => `Yes, "${contained}" at its declared position would be outside "${container}".`
159
- }
160
- ];
161
- var REACHABLE_QUESTION_TEMPLATES = [
162
- {
163
- question: (src, tgt) => `Is there a clear path from "${src}" to "${tgt}"?`,
164
- positiveAnswer: (src, tgt, pathLen) => `Yes, there is a clear path from "${src}" to "${tgt}" with a path length of ${pathLen.toFixed(1)}m.`,
165
- negativeAnswer: (src, tgt, blocker) => `No, the path from "${src}" to "${tgt}" is blocked by "${blocker}".`
166
- },
167
- {
168
- question: (src, tgt) => `Can "${src}" reach "${tgt}" without obstruction?`,
169
- positiveAnswer: (src, tgt, pathLen) => `Yes, "${src}" can reach "${tgt}" unobstructed. The path length is ${pathLen.toFixed(1)}m.`,
170
- negativeAnswer: (src, tgt, blocker) => `No, "${src}" cannot reach "${tgt}". The obstacle "${blocker}" blocks the path.`
171
- },
172
- {
173
- question: (src, tgt) => `Does the spatial_reachable constraint between "${src}" and "${tgt}" pass?`,
174
- positiveAnswer: (src, tgt, pathLen) => `The constraint passes. "${src}" can reach "${tgt}" via a ${pathLen.toFixed(1)}m path.`,
175
- negativeAnswer: (src, tgt, blocker) => `The constraint fails. "${blocker}" obstructs the path between "${src}" and "${tgt}".`
176
- },
177
- {
178
- question: (src, tgt, obstacles) => `Given obstacles [${obstacles.map((o) => `"${o}"`).join(", ")}], can "${src}" see "${tgt}"?`,
179
- positiveAnswer: (src, tgt) => `Yes, despite the obstacles, "${src}" has line of sight to "${tgt}".`,
180
- negativeAnswer: (src, tgt, blocker) => `No, "${blocker}" blocks the line of sight from "${src}" to "${tgt}".`
181
- },
182
- {
183
- question: (src, tgt) => `Evaluate whether "${src}" has an unobstructed path to "${tgt}" in this scene.`,
184
- positiveAnswer: (src, tgt, pathLen) => `"${src}" has an unobstructed path to "${tgt}" measuring ${pathLen.toFixed(1)}m.`,
185
- negativeAnswer: (src, tgt, blocker) => `"${src}" does NOT have an unobstructed path to "${tgt}". "${blocker}" is in the way.`
186
- },
187
- {
188
- question: (src, tgt) => `In this HoloScript scene, is "${tgt}" reachable from "${src}"?`,
189
- positiveAnswer: (_src, tgt, pathLen) => `Yes, "${tgt}" is reachable with a direct path of ${pathLen.toFixed(1)}m.`,
190
- negativeAnswer: (_src, tgt, blocker) => `No, "${tgt}" is not reachable. An obstacle ("${blocker}") blocks the direct path.`
191
- },
192
- {
193
- question: (src, tgt) => `Check the reachability constraint: can "${src}" navigate to "${tgt}"?`,
194
- positiveAnswer: (src, tgt, pathLen) => `Reachability check passed: "${src}" can navigate to "${tgt}" (${pathLen.toFixed(1)}m path).`,
195
- negativeAnswer: (src, tgt, blocker) => `Reachability check failed: "${src}" cannot navigate to "${tgt}" due to "${blocker}".`
196
- },
197
- {
198
- question: (src, tgt) => `Is the line of sight between "${src}" and "${tgt}" clear?`,
199
- positiveAnswer: (src, tgt) => `Yes, line of sight between "${src}" and "${tgt}" is clear.`,
200
- negativeAnswer: (src, tgt, blocker) => `No, line of sight is blocked. "${blocker}" obstructs the view from "${src}" to "${tgt}".`
201
- },
202
- {
203
- question: (src, tgt, obstacles) => `Considering ${obstacles.length} obstacle(s) in the scene, determine if "${src}" can reach "${tgt}".`,
204
- positiveAnswer: (src, tgt, pathLen) => `Despite the obstacles, "${src}" can reach "${tgt}" via a ${pathLen.toFixed(1)}m path that avoids all obstructions.`,
205
- negativeAnswer: (src, tgt, blocker) => `"${src}" cannot reach "${tgt}". The path is blocked by "${blocker}".`
206
- },
207
- {
208
- question: (src, tgt) => `Would placing a wall between "${src}" and "${tgt}" affect reachability? Analyze the current state.`,
209
- positiveAnswer: (src, tgt) => `Currently, "${src}" and "${tgt}" are mutually reachable. Adding a wall could change this.`,
210
- negativeAnswer: (src, tgt, blocker) => `Currently, "${src}" and "${tgt}" are NOT reachable due to "${blocker}". Adding more walls would not change this.`
211
- },
212
- {
213
- question: (src, tgt) => `Analyze path connectivity: does an unblocked route exist from "${src}" to "${tgt}"?`,
214
- positiveAnswer: (src, tgt, pathLen) => `An unblocked route exists from "${src}" to "${tgt}" with a total distance of ${pathLen.toFixed(1)}m.`,
215
- negativeAnswer: (src, tgt, blocker) => `No unblocked route exists from "${src}" to "${tgt}". "${blocker}" creates a barrier.`
216
- },
217
- {
218
- question: (src, tgt) => `For NPC pathfinding, can "${src}" walk to "${tgt}" in a straight line?`,
219
- positiveAnswer: (src, tgt, pathLen) => `Yes, "${src}" can walk directly to "${tgt}" in a straight line (${pathLen.toFixed(1)}m).`,
220
- negativeAnswer: (src, tgt, blocker) => `No, "${src}" cannot walk straight to "${tgt}". "${blocker}" is obstructing the direct path.`
221
- }
222
- ];
223
- var OBJECT_NAMES = [
224
- "Table",
225
- "Chair",
226
- "Lamp",
227
- "Bookshelf",
228
- "Desk",
229
- "Sofa",
230
- "Cabinet",
231
- "Mirror",
232
- "Plant",
233
- "Clock",
234
- "Vase",
235
- "Rug",
236
- "Painting",
237
- "Shelf",
238
- "Stool",
239
- "Bench",
240
- "Chest",
241
- "Barrel",
242
- "Crate",
243
- "Box",
244
- "Pillar",
245
- "Statue",
246
- "Crystal",
247
- "Orb",
248
- "Pedestal",
249
- "Platform",
250
- "Beacon",
251
- "Terminal",
252
- "Console",
253
- "Panel"
254
- ];
255
- var ZONE_NAMES = [
256
- "Room",
257
- "Hall",
258
- "Chamber",
259
- "Arena",
260
- "Gallery",
261
- "Vault",
262
- "Atrium",
263
- "Courtyard",
264
- "Alcove",
265
- "Corridor",
266
- "Laboratory",
267
- "Workshop"
268
- ];
269
- var OBSTACLE_NAMES = [
270
- "Wall",
271
- "Barrier",
272
- "Fence",
273
- "Pillar",
274
- "Column",
275
- "Boulder",
276
- "Shield",
277
- "Gate",
278
- "Blockade",
279
- "Partition",
280
- "Divider",
281
- "Screen"
282
- ];
283
- var NPC_NAMES = [
284
- "Guard",
285
- "Merchant",
286
- "Explorer",
287
- "Scout",
288
- "Villager",
289
- "Artisan",
290
- "Drone",
291
- "Robot",
292
- "Companion",
293
- "Agent",
294
- "Sentinel",
295
- "Worker"
296
- ];
297
- var GEOMETRY_TYPES = ["cube", "sphere", "cylinder", "torus", "cone", "prism"];
298
- var COLORS = [
299
- "#ff0000",
300
- "#00ff00",
301
- "#0000ff",
302
- "#ffff00",
303
- "#ff00ff",
304
- "#00ffff",
305
- "#ff8800",
306
- "#8800ff",
307
- "#00ff88",
308
- "#ff0088",
309
- "#888888",
310
- "#ffffff"
311
- ];
312
- function generateObjectBlock(obj, indent = " ") {
313
- const lines = [];
314
- lines.push(`${indent}object "${obj.id}" {`);
315
- if (obj.geometry) {
316
- lines.push(`${indent} geometry: "${obj.geometry}"`);
317
- }
318
- lines.push(
319
- `${indent} position: [${obj.position.x.toFixed(1)}, ${obj.position.y.toFixed(1)}, ${obj.position.z.toFixed(1)}]`
320
- );
321
- if (obj.scale.x !== 1 || obj.scale.y !== 1 || obj.scale.z !== 1) {
322
- lines.push(
323
- `${indent} scale: [${obj.scale.x.toFixed(1)}, ${obj.scale.y.toFixed(1)}, ${obj.scale.z.toFixed(1)}]`
324
- );
325
- }
326
- if (obj.color) {
327
- lines.push(`${indent} color: "${obj.color}"`);
328
- }
329
- lines.push(`${indent}}`);
330
- return lines.join("\n");
331
- }
332
- function generateZoneBlock(obj, indent = " ") {
333
- const lines = [];
334
- lines.push(`${indent}zone "${obj.id}" {`);
335
- lines.push(`${indent} shape: "box"`);
336
- const sx = obj.scale.x;
337
- const sy = obj.scale.y;
338
- const sz = obj.scale.z;
339
- lines.push(`${indent} size: [${sx.toFixed(1)}, ${sy.toFixed(1)}, ${sz.toFixed(1)}]`);
340
- lines.push(
341
- `${indent} position: [${obj.position.x.toFixed(1)}, ${obj.position.y.toFixed(1)}, ${obj.position.z.toFixed(1)}]`
342
- );
343
- lines.push(`${indent}}`);
344
- return lines.join("\n");
345
- }
346
- function generateAdjacentTrait(targetId, maxDist, axis) {
347
- const parts = [`target: "${targetId}"`, `maxDistance: ${maxDist.toFixed(1)}m`];
348
- if (axis && axis !== "xyz") {
349
- parts.push(`axis: "${axis}"`);
350
- }
351
- return `@spatial_adjacent(${parts.join(", ")})`;
352
- }
353
- function generateContainsTrait(targetId, margin, strict) {
354
- const parts = [`target: "${targetId}"`];
355
- if (margin !== void 0 && margin > 0) {
356
- parts.push(`margin: ${margin.toFixed(1)}m`);
357
- }
358
- if (strict) {
359
- parts.push("strict: true");
360
- }
361
- return `@spatial_contains(${parts.join(", ")})`;
362
- }
363
- function generateReachableTrait(targetId, maxPathLength, obstacles, algorithm) {
364
- const parts = [`target: "${targetId}"`];
365
- if (maxPathLength !== void 0) {
366
- parts.push(`maxPathLength: ${maxPathLength.toFixed(0)}m`);
367
- }
368
- if (obstacles && obstacles.length > 0) {
369
- parts.push(`obstacles: [${obstacles.map((o) => `"${o}"`).join(", ")}]`);
370
- }
371
- if (algorithm) {
372
- parts.push(`algorithm: "${algorithm}"`);
373
- }
374
- return `@spatial_reachable(${parts.join(", ")})`;
375
- }
376
- function computeDistance(a, b) {
377
- const dx = b.x - a.x;
378
- const dy = b.y - a.y;
379
- const dz = b.z - a.z;
380
- return Math.sqrt(dx * dx + dy * dy + dz * dz);
381
- }
382
- function isPointInBox(point, bounds, margin = 0) {
383
- return point.x >= bounds.min.x + margin && point.x <= bounds.max.x - margin && point.y >= bounds.min.y + margin && point.y <= bounds.max.y - margin && point.z >= bounds.min.z + margin && point.z <= bounds.max.z - margin;
384
- }
385
- function lineIntersectsBox(a, b, box) {
386
- const dir = { x: b.x - a.x, y: b.y - a.y, z: b.z - a.z };
387
- const len = Math.sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
388
- if (len === 0) return false;
389
- let tmin = 0;
390
- let tmax = 1;
391
- const axes = ["x", "y", "z"];
392
- for (const axis of axes) {
393
- const d = dir[axis];
394
- const o = a[axis];
395
- const bmin = box.min[axis];
396
- const bmax = box.max[axis];
397
- if (Math.abs(d) < 1e-10) {
398
- if (o < bmin || o > bmax) return false;
399
- } else {
400
- let t1 = (bmin - o) / d;
401
- let t2 = (bmax - o) / d;
402
- if (t1 > t2) {
403
- const tmp = t1;
404
- t1 = t2;
405
- t2 = tmp;
406
- }
407
- tmin = Math.max(tmin, t1);
408
- tmax = Math.min(tmax, t2);
409
- if (tmin > tmax) return false;
410
- }
411
- }
412
- return true;
413
- }
414
- var SpatialTrainingDataGenerator = class {
415
- config;
416
- rng;
417
- exampleCounter = 0;
418
- constructor(config = {}) {
419
- this.config = {
420
- examplesPerCategory: config.examplesPerCategory ?? 10,
421
- relationshipTypes: config.relationshipTypes ?? [
422
- "spatial_adjacent",
423
- "spatial_contains",
424
- "spatial_reachable"
425
- ],
426
- difficultyLevels: config.difficultyLevels ?? ["basic", "intermediate", "advanced"],
427
- positiveRatio: config.positiveRatio ?? 0.5,
428
- seed: config.seed ?? Date.now(),
429
- includeContext: config.includeContext ?? true
430
- };
431
- this.rng = new SeededRandom(this.config.seed);
432
- }
433
- // ---------------------------------------------------------------------------
434
- // Public API
435
- // ---------------------------------------------------------------------------
436
- /**
437
- * Generate all spatial training examples based on configuration.
438
- */
439
- generate() {
440
- const examples = [];
441
- this.exampleCounter = 0;
442
- for (const relType of this.config.relationshipTypes) {
443
- for (const difficulty of this.config.difficultyLevels) {
444
- const count = this.config.examplesPerCategory;
445
- for (let i = 0; i < count; i++) {
446
- const isPositive = this.rng.next() < this.config.positiveRatio;
447
- const scene = this.generateScene(relType, difficulty, isPositive);
448
- const example = this.generateExample(scene, relType, isPositive, difficulty);
449
- examples.push(example);
450
- }
451
- }
452
- }
453
- return examples;
454
- }
455
- /**
456
- * Generate examples for a specific relationship type.
457
- */
458
- generateForRelationship(relType) {
459
- const examples = [];
460
- for (const difficulty of this.config.difficultyLevels) {
461
- const count = this.config.examplesPerCategory;
462
- for (let i = 0; i < count; i++) {
463
- const isPositive = this.rng.next() < this.config.positiveRatio;
464
- const scene = this.generateScene(relType, difficulty, isPositive);
465
- const example = this.generateExample(scene, relType, isPositive, difficulty);
466
- examples.push(example);
467
- }
468
- }
469
- return examples;
470
- }
471
- /**
472
- * Generate examples for a specific difficulty level.
473
- */
474
- generateForDifficulty(difficulty) {
475
- const examples = [];
476
- for (const relType of this.config.relationshipTypes) {
477
- const count = this.config.examplesPerCategory;
478
- for (let i = 0; i < count; i++) {
479
- const isPositive = this.rng.next() < this.config.positiveRatio;
480
- const scene = this.generateScene(relType, difficulty, isPositive);
481
- const example = this.generateExample(scene, relType, isPositive, difficulty);
482
- examples.push(example);
483
- }
484
- }
485
- return examples;
486
- }
487
- /**
488
- * Export examples as JSONL string (one JSON object per line).
489
- */
490
- exportJSONL(examples) {
491
- return examples.map((ex) => {
492
- const entry = {
493
- instruction: this.config.includeContext ? `${ex.instruction}
494
-
495
- HoloScript Scene:
496
- \`\`\`holoscript
497
- ${ex.context}
498
- \`\`\`` : ex.instruction,
499
- response: ex.response,
500
- metadata: {
501
- id: ex.id,
502
- relationship_type: ex.relationshipType,
503
- is_positive: ex.isPositive,
504
- difficulty: ex.difficulty,
505
- tags: ex.tags
506
- }
507
- };
508
- return JSON.stringify(entry);
509
- }).join("\n");
510
- }
511
- /**
512
- * Export examples as JSON array string.
513
- */
514
- exportJSON(examples) {
515
- return JSON.stringify(examples, null, 2);
516
- }
517
- /**
518
- * Get statistics about generated examples.
519
- */
520
- getStats(examples) {
521
- const byRelationship = {
522
- spatial_adjacent: 0,
523
- spatial_contains: 0,
524
- spatial_reachable: 0
525
- };
526
- const byDifficulty = {
527
- basic: 0,
528
- intermediate: 0,
529
- advanced: 0
530
- };
531
- let positiveCount = 0;
532
- let negativeCount = 0;
533
- const templateIds = /* @__PURE__ */ new Set();
534
- for (const ex of examples) {
535
- byRelationship[ex.relationshipType]++;
536
- byDifficulty[ex.difficulty]++;
537
- if (ex.isPositive) positiveCount++;
538
- else negativeCount++;
539
- const tplTag = ex.tags.find((t) => t.startsWith("template:"));
540
- if (tplTag) templateIds.add(tplTag);
541
- }
542
- return {
543
- totalExamples: examples.length,
544
- byRelationship,
545
- byDifficulty,
546
- positiveCount,
547
- negativeCount,
548
- uniqueTemplatesUsed: templateIds.size
549
- };
550
- }
551
- /**
552
- * Reset the generator with a new seed.
553
- */
554
- reseed(seed) {
555
- this.rng = new SeededRandom(seed);
556
- this.exampleCounter = 0;
557
- }
558
- // ---------------------------------------------------------------------------
559
- // Scene Generation
560
- // ---------------------------------------------------------------------------
561
- /**
562
- * Generate a spatial scene for the given relationship type and difficulty.
563
- */
564
- generateScene(relType, difficulty, isPositive) {
565
- switch (relType) {
566
- case "spatial_adjacent":
567
- return this.generateAdjacentScene(difficulty, isPositive);
568
- case "spatial_contains":
569
- return this.generateContainsScene(difficulty, isPositive);
570
- case "spatial_reachable":
571
- return this.generateReachableScene(difficulty, isPositive);
572
- }
573
- }
574
- // ---------------------------------------------------------------------------
575
- // Adjacent Scene Generation
576
- // ---------------------------------------------------------------------------
577
- generateAdjacentScene(difficulty, isPositive) {
578
- const objectCount = this.getObjectCount(difficulty);
579
- const maxDistance = this.rng.float(1, 5);
580
- const objects = [];
581
- const relationships = [];
582
- const srcName = this.rng.pick(OBJECT_NAMES);
583
- const srcPos = this.randomPosition(difficulty);
584
- const srcObj = {
585
- id: srcName,
586
- type: "object",
587
- position: srcPos,
588
- scale: this.randomScale(),
589
- geometry: this.rng.pick(GEOMETRY_TYPES),
590
- color: this.rng.pick(COLORS)
591
- };
592
- objects.push(srcObj);
593
- const tgtName = this.pickUniqueName(OBJECT_NAMES, [srcName]);
594
- let tgtPos;
595
- if (isPositive) {
596
- const angle = this.rng.float(0, Math.PI * 2);
597
- const elevation = this.rng.float(-0.5, 0.5);
598
- const dist = this.rng.float(0.5, maxDistance * 0.9);
599
- tgtPos = {
600
- x: srcPos.x + Math.cos(angle) * dist,
601
- y: srcPos.y + elevation,
602
- z: srcPos.z + Math.sin(angle) * dist
603
- };
604
- } else {
605
- const angle = this.rng.float(0, Math.PI * 2);
606
- const dist = this.rng.float(maxDistance * 1.5, maxDistance * 3);
607
- tgtPos = {
608
- x: srcPos.x + Math.cos(angle) * dist,
609
- y: srcPos.y + this.rng.float(-1, 1),
610
- z: srcPos.z + Math.sin(angle) * dist
611
- };
612
- }
613
- const tgtObj = {
614
- id: tgtName,
615
- type: "object",
616
- position: tgtPos,
617
- scale: this.randomScale(),
618
- geometry: this.rng.pick(GEOMETRY_TYPES),
619
- color: this.rng.pick(COLORS)
620
- };
621
- objects.push(tgtObj);
622
- const actualDist = computeDistance(srcPos, tgtPos);
623
- relationships.push({
624
- type: "spatial_adjacent",
625
- sourceId: srcName,
626
- targetId: tgtName,
627
- satisfied: actualDist <= maxDistance,
628
- params: { maxDistance }
629
- });
630
- const usedNames = [srcName, tgtName];
631
- for (let i = 2; i < objectCount; i++) {
632
- const name = this.pickUniqueName(OBJECT_NAMES, usedNames);
633
- usedNames.push(name);
634
- objects.push({
635
- id: name,
636
- type: "object",
637
- position: this.randomPosition(difficulty),
638
- scale: this.randomScale(),
639
- geometry: this.rng.pick(GEOMETRY_TYPES),
640
- color: this.rng.pick(COLORS)
641
- });
642
- }
643
- const holoScript = this.buildAdjacentHoloScript(objects, srcName, tgtName, maxDistance);
644
- return {
645
- name: `AdjacentScene_${this.exampleCounter}`,
646
- objects,
647
- relationships,
648
- difficulty,
649
- holoScriptSource: holoScript
650
- };
651
- }
652
- buildAdjacentHoloScript(objects, srcName, tgtName, maxDistance) {
653
- const lines = [];
654
- lines.push(`composition "SpatialScene" {`);
655
- for (const obj of objects) {
656
- if (obj.id === srcName) {
657
- lines.push(` object "${obj.id}" {`);
658
- if (obj.geometry) lines.push(` geometry: "${obj.geometry}"`);
659
- lines.push(
660
- ` position: [${obj.position.x.toFixed(1)}, ${obj.position.y.toFixed(1)}, ${obj.position.z.toFixed(1)}]`
661
- );
662
- if (obj.color) lines.push(` color: "${obj.color}"`);
663
- lines.push(` ${generateAdjacentTrait(tgtName, maxDistance)}`);
664
- lines.push(" }");
665
- } else {
666
- lines.push(generateObjectBlock(obj));
667
- }
668
- lines.push("");
669
- }
670
- lines.push("}");
671
- return lines.join("\n");
672
- }
673
- // ---------------------------------------------------------------------------
674
- // Contains Scene Generation
675
- // ---------------------------------------------------------------------------
676
- generateContainsScene(difficulty, isPositive) {
677
- const objects = [];
678
- const relationships = [];
679
- const containerName = this.rng.pick(ZONE_NAMES);
680
- const containerSize = {
681
- x: this.rng.float(4, 12),
682
- y: this.rng.float(3, 8),
683
- z: this.rng.float(4, 12)
684
- };
685
- const containerPos = this.randomPosition(difficulty);
686
- const margin = this.rng.float(0, 0.5);
687
- const containerBounds = {
688
- min: {
689
- x: containerPos.x - containerSize.x / 2,
690
- y: containerPos.y - containerSize.y / 2,
691
- z: containerPos.z - containerSize.z / 2
692
- },
693
- max: {
694
- x: containerPos.x + containerSize.x / 2,
695
- y: containerPos.y + containerSize.y / 2,
696
- z: containerPos.z + containerSize.z / 2
697
- }
698
- };
699
- const containerObj = {
700
- id: containerName,
701
- type: "zone",
702
- position: containerPos,
703
- scale: containerSize,
704
- bounds: containerBounds
705
- };
706
- objects.push(containerObj);
707
- const objectCount = this.getObjectCount(difficulty);
708
- const containedName = this.rng.pick(OBJECT_NAMES);
709
- let containedPos;
710
- if (isPositive) {
711
- containedPos = {
712
- x: this.rng.float(
713
- containerBounds.min.x + margin + 0.5,
714
- containerBounds.max.x - margin - 0.5
715
- ),
716
- y: this.rng.float(
717
- containerBounds.min.y + margin + 0.5,
718
- containerBounds.max.y - margin - 0.5
719
- ),
720
- z: this.rng.float(
721
- containerBounds.min.z + margin + 0.5,
722
- containerBounds.max.z - margin - 0.5
723
- )
724
- };
725
- } else {
726
- const side = this.rng.int(0, 5);
727
- containedPos = { ...containerPos };
728
- switch (side) {
729
- case 0:
730
- containedPos.x = containerBounds.max.x + this.rng.float(1, 5);
731
- break;
732
- case 1:
733
- containedPos.x = containerBounds.min.x - this.rng.float(1, 5);
734
- break;
735
- case 2:
736
- containedPos.y = containerBounds.max.y + this.rng.float(1, 5);
737
- break;
738
- case 3:
739
- containedPos.y = containerBounds.min.y - this.rng.float(1, 5);
740
- break;
741
- case 4:
742
- containedPos.z = containerBounds.max.z + this.rng.float(1, 5);
743
- break;
744
- case 5:
745
- containedPos.z = containerBounds.min.z - this.rng.float(1, 5);
746
- break;
747
- }
748
- }
749
- const containedObj = {
750
- id: containedName,
751
- type: "object",
752
- position: containedPos,
753
- scale: this.randomScale(),
754
- geometry: this.rng.pick(GEOMETRY_TYPES),
755
- color: this.rng.pick(COLORS)
756
- };
757
- objects.push(containedObj);
758
- const actuallyContained = isPointInBox(containedPos, containerBounds, margin);
759
- relationships.push({
760
- type: "spatial_contains",
761
- sourceId: containerName,
762
- targetId: containedName,
763
- satisfied: actuallyContained,
764
- params: { margin: margin > 0 ? margin : void 0 }
765
- });
766
- const usedNames = [containerName, containedName];
767
- if (difficulty === "advanced") {
768
- const innerContainerName = this.pickUniqueName(ZONE_NAMES, usedNames);
769
- usedNames.push(innerContainerName);
770
- const innerSize = {
771
- x: containerSize.x * 0.4,
772
- y: containerSize.y * 0.4,
773
- z: containerSize.z * 0.4
774
- };
775
- const innerObj = {
776
- id: innerContainerName,
777
- type: "zone",
778
- position: { ...containerPos },
779
- scale: innerSize,
780
- bounds: {
781
- min: {
782
- x: containerPos.x - innerSize.x / 2,
783
- y: containerPos.y - innerSize.y / 2,
784
- z: containerPos.z - innerSize.z / 2
785
- },
786
- max: {
787
- x: containerPos.x + innerSize.x / 2,
788
- y: containerPos.y + innerSize.y / 2,
789
- z: containerPos.z + innerSize.z / 2
790
- }
791
- }
792
- };
793
- objects.push(innerObj);
794
- }
795
- for (let i = objects.length; i < objectCount; i++) {
796
- const name = this.pickUniqueName(OBJECT_NAMES, usedNames);
797
- usedNames.push(name);
798
- objects.push({
799
- id: name,
800
- type: "object",
801
- position: this.randomPosition(difficulty),
802
- scale: this.randomScale(),
803
- geometry: this.rng.pick(GEOMETRY_TYPES),
804
- color: this.rng.pick(COLORS)
805
- });
806
- }
807
- const holoScript = this.buildContainsHoloScript(objects, containerName, containedName, margin);
808
- return {
809
- name: `ContainsScene_${this.exampleCounter}`,
810
- objects,
811
- relationships,
812
- difficulty,
813
- holoScriptSource: holoScript
814
- };
815
- }
816
- buildContainsHoloScript(objects, containerName, containedName, margin) {
817
- const lines = [];
818
- lines.push(`composition "SpatialScene" {`);
819
- for (const obj of objects) {
820
- if (obj.type === "zone") {
821
- if (obj.id === containerName) {
822
- lines.push(` zone "${obj.id}" {`);
823
- lines.push(` shape: "box"`);
824
- lines.push(
825
- ` size: [${obj.scale.x.toFixed(1)}, ${obj.scale.y.toFixed(1)}, ${obj.scale.z.toFixed(1)}]`
826
- );
827
- lines.push(
828
- ` position: [${obj.position.x.toFixed(1)}, ${obj.position.y.toFixed(1)}, ${obj.position.z.toFixed(1)}]`
829
- );
830
- lines.push(
831
- ` ${generateContainsTrait(containedName, margin > 0 ? margin : void 0)}`
832
- );
833
- lines.push(" }");
834
- } else {
835
- lines.push(generateZoneBlock(obj));
836
- }
837
- } else {
838
- lines.push(generateObjectBlock(obj));
839
- }
840
- lines.push("");
841
- }
842
- lines.push("}");
843
- return lines.join("\n");
844
- }
845
- // ---------------------------------------------------------------------------
846
- // Reachable Scene Generation
847
- // ---------------------------------------------------------------------------
848
- generateReachableScene(difficulty, isPositive) {
849
- const objectCount = this.getObjectCount(difficulty);
850
- const objects = [];
851
- const relationships = [];
852
- const srcName = this.rng.pick(NPC_NAMES);
853
- const srcPos = this.randomPosition(difficulty);
854
- objects.push({
855
- id: srcName,
856
- type: "npc",
857
- position: srcPos,
858
- scale: { x: 1, y: 1, z: 1 },
859
- geometry: "sphere",
860
- color: this.rng.pick(COLORS)
861
- });
862
- const tgtName = this.pickUniqueName([...OBJECT_NAMES, ...NPC_NAMES], [srcName]);
863
- const tgtAngle = this.rng.float(0, Math.PI * 2);
864
- const tgtDist = this.rng.float(5, 20);
865
- const tgtPos = {
866
- x: srcPos.x + Math.cos(tgtAngle) * tgtDist,
867
- y: srcPos.y + this.rng.float(-1, 1),
868
- z: srcPos.z + Math.sin(tgtAngle) * tgtDist
869
- };
870
- objects.push({
871
- id: tgtName,
872
- type: "object",
873
- position: tgtPos,
874
- scale: this.randomScale(),
875
- geometry: this.rng.pick(GEOMETRY_TYPES),
876
- color: this.rng.pick(COLORS)
877
- });
878
- const obstacleCount = difficulty === "basic" ? 0 : difficulty === "intermediate" ? this.rng.int(1, 2) : this.rng.int(2, 4);
879
- const obstacleNames = [];
880
- const usedNames = [srcName, tgtName];
881
- let blockingObstacle = null;
882
- for (let i = 0; i < obstacleCount; i++) {
883
- const obsName = this.pickUniqueName(OBSTACLE_NAMES, usedNames);
884
- usedNames.push(obsName);
885
- obstacleNames.push(obsName);
886
- const obsScale = {
887
- x: this.rng.float(1.5, 4),
888
- y: this.rng.float(2, 5),
889
- z: this.rng.float(1.5, 4)
890
- };
891
- let obsPos;
892
- if (!isPositive && i === 0) {
893
- const t = this.rng.float(0.3, 0.7);
894
- obsPos = {
895
- x: srcPos.x + (tgtPos.x - srcPos.x) * t,
896
- y: srcPos.y + (tgtPos.y - srcPos.y) * t,
897
- z: srcPos.z + (tgtPos.z - srcPos.z) * t
898
- };
899
- blockingObstacle = obsName;
900
- } else {
901
- const offset = this.rng.float(3, 8);
902
- const side = this.rng.next() > 0.5 ? 1 : -1;
903
- const midpoint = {
904
- x: (srcPos.x + tgtPos.x) / 2,
905
- y: (srcPos.y + tgtPos.y) / 2,
906
- z: (srcPos.z + tgtPos.z) / 2
907
- };
908
- const perpX = -(tgtPos.z - srcPos.z);
909
- const perpZ = tgtPos.x - srcPos.x;
910
- const perpLen = Math.sqrt(perpX * perpX + perpZ * perpZ);
911
- if (perpLen > 0) {
912
- obsPos = {
913
- x: midpoint.x + perpX / perpLen * offset * side,
914
- y: midpoint.y,
915
- z: midpoint.z + perpZ / perpLen * offset * side
916
- };
917
- } else {
918
- obsPos = {
919
- x: midpoint.x + offset * side,
920
- y: midpoint.y,
921
- z: midpoint.z
922
- };
923
- }
924
- }
925
- const obsBounds = {
926
- min: {
927
- x: obsPos.x - obsScale.x / 2,
928
- y: obsPos.y - obsScale.y / 2,
929
- z: obsPos.z - obsScale.z / 2
930
- },
931
- max: {
932
- x: obsPos.x + obsScale.x / 2,
933
- y: obsPos.y + obsScale.y / 2,
934
- z: obsPos.z + obsScale.z / 2
935
- }
936
- };
937
- objects.push({
938
- id: obsName,
939
- type: "obstacle",
940
- position: obsPos,
941
- scale: obsScale,
942
- bounds: obsBounds,
943
- isObstacle: true,
944
- geometry: "cube",
945
- color: "#444444"
946
- });
947
- }
948
- let actuallyReachable = true;
949
- if (obstacleNames.length > 0) {
950
- for (const obj of objects) {
951
- if (obj.isObstacle && obj.bounds) {
952
- if (lineIntersectsBox(srcPos, tgtPos, obj.bounds)) {
953
- actuallyReachable = false;
954
- if (!blockingObstacle) blockingObstacle = obj.id;
955
- break;
956
- }
957
- }
958
- }
959
- }
960
- const maxPathLength = this.rng.float(tgtDist * 1.2, tgtDist * 2);
961
- relationships.push({
962
- type: "spatial_reachable",
963
- sourceId: srcName,
964
- targetId: tgtName,
965
- satisfied: actuallyReachable,
966
- params: {
967
- maxPathLength,
968
- obstacleTypes: obstacleNames.length > 0 ? ["obstacle"] : void 0,
969
- algorithm: "line_of_sight"
970
- }
971
- });
972
- for (let i = objects.length; i < objectCount; i++) {
973
- const name = this.pickUniqueName(OBJECT_NAMES, usedNames);
974
- usedNames.push(name);
975
- objects.push({
976
- id: name,
977
- type: "object",
978
- position: this.randomPosition(difficulty),
979
- scale: this.randomScale(),
980
- geometry: this.rng.pick(GEOMETRY_TYPES),
981
- color: this.rng.pick(COLORS)
982
- });
983
- }
984
- const holoScript = this.buildReachableHoloScript(
985
- objects,
986
- srcName,
987
- tgtName,
988
- maxPathLength,
989
- obstacleNames
990
- );
991
- return {
992
- name: `ReachableScene_${this.exampleCounter}`,
993
- objects,
994
- relationships,
995
- difficulty,
996
- holoScriptSource: holoScript
997
- };
998
- }
999
- buildReachableHoloScript(objects, srcName, tgtName, maxPathLength, obstacleNames) {
1000
- const lines = [];
1001
- lines.push(`composition "SpatialScene" {`);
1002
- for (const obj of objects) {
1003
- if (obj.id === srcName) {
1004
- lines.push(` object "${obj.id}" {`);
1005
- if (obj.geometry) lines.push(` geometry: "${obj.geometry}"`);
1006
- lines.push(
1007
- ` position: [${obj.position.x.toFixed(1)}, ${obj.position.y.toFixed(1)}, ${obj.position.z.toFixed(1)}]`
1008
- );
1009
- if (obj.color) lines.push(` color: "${obj.color}"`);
1010
- lines.push(
1011
- ` ${generateReachableTrait(tgtName, maxPathLength, obstacleNames.length > 0 ? ["obstacle"] : void 0, "line_of_sight")}`
1012
- );
1013
- lines.push(" }");
1014
- } else if (obj.isObstacle) {
1015
- lines.push(` object "${obj.id}" {`);
1016
- lines.push(` geometry: "cube"`);
1017
- lines.push(
1018
- ` position: [${obj.position.x.toFixed(1)}, ${obj.position.y.toFixed(1)}, ${obj.position.z.toFixed(1)}]`
1019
- );
1020
- lines.push(
1021
- ` scale: [${obj.scale.x.toFixed(1)}, ${obj.scale.y.toFixed(1)}, ${obj.scale.z.toFixed(1)}]`
1022
- );
1023
- lines.push(" @static");
1024
- lines.push(" @collidable");
1025
- lines.push(" }");
1026
- } else {
1027
- lines.push(generateObjectBlock(obj));
1028
- }
1029
- lines.push("");
1030
- }
1031
- lines.push("}");
1032
- return lines.join("\n");
1033
- }
1034
- // ---------------------------------------------------------------------------
1035
- // Example Generation (instruction-response pairs)
1036
- // ---------------------------------------------------------------------------
1037
- generateExample(scene, relType, isPositive, difficulty) {
1038
- this.exampleCounter++;
1039
- const rel = scene.relationships[0];
1040
- let instruction;
1041
- let response;
1042
- let templateIndex;
1043
- switch (relType) {
1044
- case "spatial_adjacent": {
1045
- templateIndex = this.rng.int(0, ADJACENT_QUESTION_TEMPLATES.length - 1);
1046
- const template = ADJACENT_QUESTION_TEMPLATES[templateIndex];
1047
- const dist = computeDistance(
1048
- scene.objects.find((o) => o.id === rel.sourceId).position,
1049
- scene.objects.find((o) => o.id === rel.targetId).position
1050
- );
1051
- instruction = template.question(rel.sourceId, rel.targetId, dist, rel.params.maxDistance);
1052
- response = isPositive ? template.positiveAnswer(rel.sourceId, rel.targetId, dist, rel.params.maxDistance) : template.negativeAnswer(rel.sourceId, rel.targetId, dist, rel.params.maxDistance);
1053
- break;
1054
- }
1055
- case "spatial_contains": {
1056
- templateIndex = this.rng.int(0, CONTAINS_QUESTION_TEMPLATES.length - 1);
1057
- const template = CONTAINS_QUESTION_TEMPLATES[templateIndex];
1058
- instruction = template.question(rel.sourceId, rel.targetId);
1059
- response = isPositive ? template.positiveAnswer(rel.sourceId, rel.targetId) : template.negativeAnswer(rel.sourceId, rel.targetId);
1060
- break;
1061
- }
1062
- case "spatial_reachable": {
1063
- templateIndex = this.rng.int(0, REACHABLE_QUESTION_TEMPLATES.length - 1);
1064
- const template = REACHABLE_QUESTION_TEMPLATES[templateIndex];
1065
- const obstacles = scene.objects.filter((o) => o.isObstacle).map((o) => o.id);
1066
- const pathLen = computeDistance(
1067
- scene.objects.find((o) => o.id === rel.sourceId).position,
1068
- scene.objects.find((o) => o.id === rel.targetId).position
1069
- );
1070
- const blocker = scene.objects.find((o) => o.isObstacle)?.id ?? "unknown";
1071
- instruction = template.question(rel.sourceId, rel.targetId, obstacles);
1072
- response = isPositive ? template.positiveAnswer(rel.sourceId, rel.targetId, pathLen) : template.negativeAnswer(rel.sourceId, rel.targetId, blocker);
1073
- break;
1074
- }
1075
- }
1076
- return {
1077
- id: `spatial-${relType.replace("spatial_", "")}-${difficulty}-${this.exampleCounter}`,
1078
- instruction,
1079
- response,
1080
- context: scene.holoScriptSource,
1081
- relationshipType: relType,
1082
- isPositive,
1083
- difficulty,
1084
- tags: [
1085
- relType,
1086
- difficulty,
1087
- isPositive ? "positive" : "negative",
1088
- `template:${relType}-${templateIndex}`
1089
- ]
1090
- };
1091
- }
1092
- // ---------------------------------------------------------------------------
1093
- // Utility Methods
1094
- // ---------------------------------------------------------------------------
1095
- getObjectCount(difficulty) {
1096
- switch (difficulty) {
1097
- case "basic":
1098
- return 2;
1099
- case "intermediate":
1100
- return this.rng.int(3, 5);
1101
- case "advanced":
1102
- return this.rng.int(6, 9);
1103
- }
1104
- }
1105
- randomPosition(difficulty) {
1106
- const range = difficulty === "basic" ? 5 : difficulty === "intermediate" ? 10 : 20;
1107
- return {
1108
- x: this.rng.float(-range, range),
1109
- y: this.rng.float(0, range / 2),
1110
- z: this.rng.float(-range, range)
1111
- };
1112
- }
1113
- randomScale() {
1114
- const uniform = this.rng.next() > 0.5;
1115
- if (uniform) {
1116
- const s = this.rng.float(0.3, 2.5);
1117
- return { x: s, y: s, z: s };
1118
- }
1119
- return {
1120
- x: this.rng.float(0.3, 3),
1121
- y: this.rng.float(0.3, 3),
1122
- z: this.rng.float(0.3, 3)
1123
- };
1124
- }
1125
- pickUniqueName(pool, used) {
1126
- const available = pool.filter((n) => !used.includes(n));
1127
- if (available.length === 0) {
1128
- return `${this.rng.pick(pool)}_${this.rng.int(100, 999)}`;
1129
- }
1130
- return this.rng.pick(available);
1131
- }
1132
- };
1133
- function createSpatialTrainingDataGenerator(config) {
1134
- return new SpatialTrainingDataGenerator(config);
1135
- }
1136
-
1137
- // src/training/SparsityMonitor.ts
1138
- var DEFAULT_CONFIG = {
1139
- sparsityThreshold: 0.93,
1140
- windowSize: 50,
1141
- perLayerTracking: true,
1142
- energyMetricsEnabled: true,
1143
- avgSynapsesPerNeuron: 100,
1144
- opsPerSynapse: 2,
1145
- criticalThreshold: 0.85,
1146
- maxViolationHistory: 1e3
1147
- };
1148
- var SparsityMonitor = class {
1149
- config;
1150
- /** Current layer metrics indexed by layerId, latest values per layer */
1151
- currentLayerMetrics = /* @__PURE__ */ new Map();
1152
- /** Historical layer metrics: layerId -> array of metrics over time */
1153
- layerHistory = /* @__PURE__ */ new Map();
1154
- /** Snapshots taken over time */
1155
- snapshots = [];
1156
- /** Detected violations */
1157
- violations = [];
1158
- /** Rolling window of aggregate sparsity values for stats */
1159
- sparsityWindow = [];
1160
- /** Total timesteps recorded across all layers */
1161
- totalTimestepsRecorded = 0;
1162
- constructor(config = {}) {
1163
- this.config = { ...DEFAULT_CONFIG, ...config };
1164
- }
1165
- // ---------------------------------------------------------------------------
1166
- // Recording
1167
- // ---------------------------------------------------------------------------
1168
- /**
1169
- * Record activity for a single SNN layer at a given timestep.
1170
- *
1171
- * Computes spike rate and activation sparsity from the raw input,
1172
- * checks for threshold violations, and stores the metrics.
1173
- *
1174
- * @param layerId - Unique identifier for the layer
1175
- * @param input - Raw activity data (neuronCount, spikeCount, timestep)
1176
- * @returns The computed SNNLayerMetrics for this recording
1177
- */
1178
- recordLayerActivity(layerId, input) {
1179
- if (input.neuronCount <= 0) {
1180
- throw new Error(`neuronCount must be positive, got ${input.neuronCount}`);
1181
- }
1182
- if (input.spikeCount < 0) {
1183
- throw new Error(`spikeCount must be non-negative, got ${input.spikeCount}`);
1184
- }
1185
- if (input.spikeCount > input.neuronCount) {
1186
- throw new Error(
1187
- `spikeCount (${input.spikeCount}) cannot exceed neuronCount (${input.neuronCount})`
1188
- );
1189
- }
1190
- const spikeRate = input.spikeCount / input.neuronCount;
1191
- const activationSparsity = 1 - spikeRate;
1192
- const metrics = {
1193
- layerId,
1194
- neuronCount: input.neuronCount,
1195
- spikeCount: input.spikeCount,
1196
- spikeRate: roundTo4(spikeRate),
1197
- activationSparsity: roundTo4(activationSparsity),
1198
- avgMembranePotential: input.avgMembranePotential,
1199
- timestep: input.timestep
1200
- };
1201
- this.currentLayerMetrics.set(layerId, metrics);
1202
- if (this.config.perLayerTracking) {
1203
- if (!this.layerHistory.has(layerId)) {
1204
- this.layerHistory.set(layerId, []);
1205
- }
1206
- this.layerHistory.get(layerId).push(metrics);
1207
- }
1208
- this.totalTimestepsRecorded++;
1209
- this.checkViolation(metrics);
1210
- return metrics;
1211
- }
1212
- /**
1213
- * Record activity for multiple layers at the same timestep (batch recording).
1214
- *
1215
- * @param layerInputs - Map of layerId -> activity input
1216
- * @returns Array of computed metrics for each layer
1217
- */
1218
- recordBatchActivity(layerInputs) {
1219
- const entries = layerInputs instanceof Map ? Array.from(layerInputs.entries()) : Object.entries(layerInputs);
1220
- return entries.map(([layerId, input]) => this.recordLayerActivity(layerId, input));
1221
- }
1222
- // ---------------------------------------------------------------------------
1223
- // Snapshots
1224
- // ---------------------------------------------------------------------------
1225
- /**
1226
- * Take a point-in-time snapshot of all current layer metrics.
1227
- *
1228
- * The snapshot captures aggregate statistics across all tracked layers,
1229
- * computes energy efficiency, and checks for violations.
1230
- *
1231
- * @returns The snapshot, or null if no layer metrics have been recorded
1232
- */
1233
- takeSnapshot() {
1234
- if (this.currentLayerMetrics.size === 0) {
1235
- return null;
1236
- }
1237
- const layers = Array.from(this.currentLayerMetrics.values());
1238
- const totalNeurons = layers.reduce((sum, l) => sum + l.neuronCount, 0);
1239
- const totalSpikes = layers.reduce((sum, l) => sum + l.spikeCount, 0);
1240
- const aggregateSparsity = totalNeurons > 0 ? roundTo4(1 - totalSpikes / totalNeurons) : 1;
1241
- const aggregateSpikeRate = totalNeurons > 0 ? roundTo4(totalSpikes / totalNeurons) : 0;
1242
- const energyEfficiency = this.config.energyMetricsEnabled ? this.calculateEnergyEfficiency(layers) : createZeroEnergyMetrics();
1243
- const violations = this.detectViolationsForLayers(layers);
1244
- const snapshot = {
1245
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1246
- layers: layers.map((l) => ({ ...l })),
1247
- aggregateSparsity,
1248
- aggregateSpikeRate,
1249
- totalNeurons,
1250
- totalSpikes,
1251
- energyEfficiency,
1252
- violations
1253
- };
1254
- this.snapshots.push(snapshot);
1255
- this.sparsityWindow.push(aggregateSparsity);
1256
- if (this.sparsityWindow.length > this.config.windowSize) {
1257
- this.sparsityWindow.shift();
1258
- }
1259
- return snapshot;
1260
- }
1261
- // ---------------------------------------------------------------------------
1262
- // Energy Efficiency
1263
- // ---------------------------------------------------------------------------
1264
- /**
1265
- * Calculate theoretical energy efficiency metrics for the given layers.
1266
- *
1267
- * Dense ops = sum of (neuronCount * avgSynapsesPerNeuron * opsPerSynapse) per layer
1268
- * Sparse ops = sum of (spikeCount * avgSynapsesPerNeuron * opsPerSynapse) per layer
1269
- *
1270
- * The ratio of ops saved reflects the theoretical computational advantage
1271
- * of SNN sparsity over equivalent dense ANN computation.
1272
- */
1273
- calculateEnergyEfficiency(layers) {
1274
- const { avgSynapsesPerNeuron, opsPerSynapse } = this.config;
1275
- let denseOps = 0;
1276
- let sparseOps = 0;
1277
- for (const layer of layers) {
1278
- const layerDenseOps = layer.neuronCount * avgSynapsesPerNeuron * opsPerSynapse;
1279
- const layerSparseOps = layer.spikeCount * avgSynapsesPerNeuron * opsPerSynapse;
1280
- denseOps += layerDenseOps;
1281
- sparseOps += layerSparseOps;
1282
- }
1283
- const opsSaved = denseOps - sparseOps;
1284
- const efficiencyRatio = denseOps > 0 ? roundTo4(opsSaved / denseOps) : 0;
1285
- const energySavingsFactor = denseOps > 0 ? roundTo4(denseOps / Math.max(1, sparseOps)) : 1;
1286
- return {
1287
- denseOps,
1288
- sparseOps,
1289
- opsSaved,
1290
- efficiencyRatio,
1291
- energySavingsFactor
1292
- };
1293
- }
1294
- // ---------------------------------------------------------------------------
1295
- // Violation Detection
1296
- // ---------------------------------------------------------------------------
1297
- /**
1298
- * Check a single layer's metrics against the sparsity threshold.
1299
- * If a violation is detected, it is recorded in the violations history.
1300
- */
1301
- checkViolation(metrics) {
1302
- if (metrics.activationSparsity >= this.config.sparsityThreshold) {
1303
- return null;
1304
- }
1305
- const deficit = roundTo4(this.config.sparsityThreshold - metrics.activationSparsity);
1306
- const severity = metrics.activationSparsity < this.config.criticalThreshold ? "critical" : "warning";
1307
- const violation = {
1308
- layerId: metrics.layerId,
1309
- measuredSparsity: metrics.activationSparsity,
1310
- requiredThreshold: this.config.sparsityThreshold,
1311
- deficit,
1312
- severity,
1313
- detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
1314
- timestep: metrics.timestep
1315
- };
1316
- this.violations.push(violation);
1317
- if (this.violations.length > this.config.maxViolationHistory) {
1318
- this.violations = this.violations.slice(-this.config.maxViolationHistory);
1319
- }
1320
- return violation;
1321
- }
1322
- /**
1323
- * Detect violations for an array of layer metrics (used in snapshots).
1324
- */
1325
- detectViolationsForLayers(layers) {
1326
- const snapshotViolations = [];
1327
- for (const layer of layers) {
1328
- if (layer.activationSparsity < this.config.sparsityThreshold) {
1329
- const deficit = roundTo4(this.config.sparsityThreshold - layer.activationSparsity);
1330
- const severity = layer.activationSparsity < this.config.criticalThreshold ? "critical" : "warning";
1331
- snapshotViolations.push({
1332
- layerId: layer.layerId,
1333
- measuredSparsity: layer.activationSparsity,
1334
- requiredThreshold: this.config.sparsityThreshold,
1335
- deficit,
1336
- severity,
1337
- detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
1338
- timestep: layer.timestep
1339
- });
1340
- }
1341
- }
1342
- return snapshotViolations;
1343
- }
1344
- /**
1345
- * Get all violations currently affecting active layers
1346
- * (the most recent metric per layer that is below threshold).
1347
- */
1348
- getActiveViolations() {
1349
- const active = [];
1350
- for (const [, metrics] of this.currentLayerMetrics) {
1351
- if (metrics.activationSparsity < this.config.sparsityThreshold) {
1352
- const deficit = roundTo4(this.config.sparsityThreshold - metrics.activationSparsity);
1353
- const severity = metrics.activationSparsity < this.config.criticalThreshold ? "critical" : "warning";
1354
- active.push({
1355
- layerId: metrics.layerId,
1356
- measuredSparsity: metrics.activationSparsity,
1357
- requiredThreshold: this.config.sparsityThreshold,
1358
- deficit,
1359
- severity,
1360
- detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
1361
- timestep: metrics.timestep
1362
- });
1363
- }
1364
- }
1365
- return active;
1366
- }
1367
- /**
1368
- * Get all historical violations.
1369
- */
1370
- getViolationHistory() {
1371
- return [...this.violations];
1372
- }
1373
- // ---------------------------------------------------------------------------
1374
- // Statistics
1375
- // ---------------------------------------------------------------------------
1376
- /**
1377
- * Compute aggregate statistics across all recorded data.
1378
- */
1379
- getStats() {
1380
- const allSparsities = [];
1381
- const perLayerSums = {};
1382
- for (const [layerId, history] of this.layerHistory) {
1383
- for (const m of history) {
1384
- allSparsities.push(m.activationSparsity);
1385
- if (!perLayerSums[layerId]) {
1386
- perLayerSums[layerId] = { sum: 0, count: 0 };
1387
- }
1388
- perLayerSums[layerId].sum += m.activationSparsity;
1389
- perLayerSums[layerId].count++;
1390
- }
1391
- }
1392
- if (allSparsities.length === 0) {
1393
- for (const [layerId, m] of this.currentLayerMetrics) {
1394
- allSparsities.push(m.activationSparsity);
1395
- perLayerSums[layerId] = { sum: m.activationSparsity, count: 1 };
1396
- }
1397
- }
1398
- const meanSparsity = allSparsities.length > 0 ? roundTo4(allSparsities.reduce((a, b) => a + b, 0) / allSparsities.length) : 0;
1399
- const minSparsity = allSparsities.length > 0 ? Math.min(...allSparsities) : 0;
1400
- const maxSparsity = allSparsities.length > 0 ? Math.max(...allSparsities) : 0;
1401
- const stdDevSparsity = allSparsities.length > 1 ? roundTo4(standardDeviation(allSparsities)) : 0;
1402
- const perLayerMeanSparsity = {};
1403
- for (const [layerId, data] of Object.entries(perLayerSums)) {
1404
- perLayerMeanSparsity[layerId] = roundTo4(data.sum / data.count);
1405
- }
1406
- const warningCount = this.violations.filter((v) => v.severity === "warning").length;
1407
- const criticalCount = this.violations.filter((v) => v.severity === "critical").length;
1408
- const efficiencies = this.snapshots.map((s) => s.energyEfficiency.efficiencyRatio).filter((e) => e > 0);
1409
- const meanEnergyEfficiency = efficiencies.length > 0 ? roundTo4(efficiencies.reduce((a, b) => a + b, 0) / efficiencies.length) : 0;
1410
- const activeViolations = this.getActiveViolations();
1411
- return {
1412
- totalTimesteps: this.totalTimestepsRecorded,
1413
- totalSnapshots: this.snapshots.length,
1414
- trackedLayers: this.currentLayerMetrics.size,
1415
- meanSparsity,
1416
- minSparsity,
1417
- maxSparsity,
1418
- stdDevSparsity,
1419
- totalViolations: this.violations.length,
1420
- violationsBySeverity: {
1421
- warning: warningCount,
1422
- critical: criticalCount
1423
- },
1424
- perLayerMeanSparsity,
1425
- meanEnergyEfficiency,
1426
- inCompliance: activeViolations.length === 0
1427
- };
1428
- }
1429
- // ---------------------------------------------------------------------------
1430
- // Quality History Integration
1431
- // ---------------------------------------------------------------------------
1432
- /**
1433
- * Generate an entry compatible with the quality-history.json format.
1434
- *
1435
- * The composite score is based on the aggregate sparsity relative to
1436
- * the threshold: score = min(1, aggregateSparsity / threshold).
1437
- *
1438
- * @param cycle - The monitoring cycle number
1439
- * @returns A quality history entry with sparsity-specific metrics
1440
- */
1441
- toQualityHistoryEntry(cycle) {
1442
- const stats = this.getStats();
1443
- const latestSnapshot = this.snapshots.length > 0 ? this.snapshots[this.snapshots.length - 1] : null;
1444
- const aggregateSparsity = latestSnapshot?.aggregateSparsity ?? stats.meanSparsity;
1445
- const aggregateSpikeRate = latestSnapshot?.aggregateSpikeRate ?? 1 - stats.meanSparsity;
1446
- const composite = roundTo4(Math.min(1, aggregateSparsity / this.config.sparsityThreshold));
1447
- const grade = gradeFromComposite(composite);
1448
- const summary = this.generateSummary(stats, latestSnapshot, composite, grade);
1449
- return {
1450
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1451
- cycle,
1452
- composite,
1453
- grade,
1454
- focus: "snn-sparsity",
1455
- summary,
1456
- sparsityMetrics: {
1457
- aggregateSparsity,
1458
- aggregateSpikeRate: roundTo4(aggregateSpikeRate),
1459
- energyEfficiencyRatio: stats.meanEnergyEfficiency,
1460
- violationCount: stats.totalViolations,
1461
- layerCount: stats.trackedLayers,
1462
- totalNeurons: latestSnapshot?.totalNeurons ?? 0,
1463
- inCompliance: stats.inCompliance
1464
- }
1465
- };
1466
- }
1467
- /**
1468
- * Generate a human-readable summary string for the quality history entry.
1469
- */
1470
- generateSummary(stats, latestSnapshot, composite, grade) {
1471
- const lines = [];
1472
- lines.push(`SNN Sparsity Monitor - Grade: ${grade} (${(composite * 100).toFixed(1)}%)`);
1473
- lines.push(`Layers: ${stats.trackedLayers}, Timesteps: ${stats.totalTimesteps}`);
1474
- lines.push(
1475
- `Mean Sparsity: ${(stats.meanSparsity * 100).toFixed(1)}% (threshold: ${(this.config.sparsityThreshold * 100).toFixed(0)}%)`
1476
- );
1477
- lines.push(
1478
- `Range: [${(stats.minSparsity * 100).toFixed(1)}%, ${(stats.maxSparsity * 100).toFixed(1)}%]`
1479
- );
1480
- if (latestSnapshot) {
1481
- const eff = latestSnapshot.energyEfficiency;
1482
- lines.push(
1483
- `Energy Efficiency: ${(eff.efficiencyRatio * 100).toFixed(1)}% ops saved (${eff.energySavingsFactor.toFixed(1)}x factor)`
1484
- );
1485
- }
1486
- if (stats.totalViolations > 0) {
1487
- lines.push(
1488
- `Violations: ${stats.totalViolations} (${stats.violationsBySeverity.critical} critical, ${stats.violationsBySeverity.warning} warning)`
1489
- );
1490
- } else {
1491
- lines.push("Compliance: All layers within threshold");
1492
- }
1493
- return lines.join("\n");
1494
- }
1495
- // ---------------------------------------------------------------------------
1496
- // Harvester Integration
1497
- // ---------------------------------------------------------------------------
1498
- /**
1499
- * Get metrics formatted for integration with SelfImproveHarvester.
1500
- *
1501
- * Returns a record of key metrics that can be attached to harvest records
1502
- * as additional metadata for training data quality assessment.
1503
- */
1504
- getHarvesterMetrics() {
1505
- const stats = this.getStats();
1506
- return {
1507
- snn_mean_sparsity: stats.meanSparsity,
1508
- snn_min_sparsity: stats.minSparsity,
1509
- snn_max_sparsity: stats.maxSparsity,
1510
- snn_violation_count: stats.totalViolations,
1511
- snn_energy_efficiency: stats.meanEnergyEfficiency,
1512
- snn_in_compliance: stats.inCompliance,
1513
- snn_tracked_layers: stats.trackedLayers,
1514
- snn_total_timesteps: stats.totalTimesteps
1515
- };
1516
- }
1517
- // ---------------------------------------------------------------------------
1518
- // Accessors
1519
- // ---------------------------------------------------------------------------
1520
- /**
1521
- * Get all recorded snapshots.
1522
- */
1523
- getSnapshots() {
1524
- return [...this.snapshots];
1525
- }
1526
- /**
1527
- * Get the most recent snapshot, or null if none have been taken.
1528
- */
1529
- getLatestSnapshot() {
1530
- return this.snapshots.length > 0 ? { ...this.snapshots[this.snapshots.length - 1] } : null;
1531
- }
1532
- /**
1533
- * Get current layer metrics.
1534
- */
1535
- getCurrentLayerMetrics() {
1536
- return new Map(this.currentLayerMetrics);
1537
- }
1538
- /**
1539
- * Get history for a specific layer.
1540
- */
1541
- getLayerHistory(layerId) {
1542
- return [...this.layerHistory.get(layerId) ?? []];
1543
- }
1544
- /**
1545
- * Get the current configuration.
1546
- */
1547
- getConfig() {
1548
- return { ...this.config };
1549
- }
1550
- // ---------------------------------------------------------------------------
1551
- // Reset
1552
- // ---------------------------------------------------------------------------
1553
- /**
1554
- * Reset all recorded data and start fresh.
1555
- */
1556
- reset() {
1557
- this.currentLayerMetrics.clear();
1558
- this.layerHistory.clear();
1559
- this.snapshots = [];
1560
- this.violations = [];
1561
- this.sparsityWindow = [];
1562
- this.totalTimestepsRecorded = 0;
1563
- }
1564
- };
1565
- function roundTo4(value) {
1566
- return Math.round(value * 1e4) / 1e4;
1567
- }
1568
- function standardDeviation(values) {
1569
- if (values.length < 2) return 0;
1570
- const mean = values.reduce((a, b) => a + b, 0) / values.length;
1571
- const squaredDiffs = values.map((v) => (v - mean) ** 2);
1572
- const variance = squaredDiffs.reduce((a, b) => a + b, 0) / (values.length - 1);
1573
- return Math.sqrt(variance);
1574
- }
1575
- function gradeFromComposite(composite) {
1576
- if (composite >= 0.95) return "A";
1577
- if (composite >= 0.85) return "B";
1578
- if (composite >= 0.7) return "C";
1579
- if (composite >= 0.5) return "D";
1580
- return "F";
1581
- }
1582
- function createZeroEnergyMetrics() {
1583
- return {
1584
- denseOps: 0,
1585
- sparseOps: 0,
1586
- opsSaved: 0,
1587
- efficiencyRatio: 0,
1588
- energySavingsFactor: 1
1589
- };
1590
- }
1591
- function createSparsityMonitor(config) {
1592
- return new SparsityMonitor(config);
1593
- }
1594
-
1595
- // src/training/SoftDedup.ts
1596
- var DEFAULT_SOFTDEDUP_CONFIG = {
1597
- ngramSizes: [3, 5, 7],
1598
- wordLevel: false,
1599
- minWeight: 0.1,
1600
- maxWeight: 1,
1601
- temperature: 1,
1602
- commonThresholdPercentile: 0.7
1603
- };
1604
- var SoftDedup = class {
1605
- config;
1606
- constructor(config = {}) {
1607
- this.config = { ...DEFAULT_SOFTDEDUP_CONFIG, ...config };
1608
- this.validateConfig();
1609
- }
1610
- /**
1611
- * Process a dataset of text examples and compute sampling weights.
1612
- *
1613
- * @param examples - Array of text strings (training examples)
1614
- * @returns Array of SoftDedupResult with sampling weights
1615
- */
1616
- process(examples) {
1617
- if (examples.length === 0) {
1618
- return [];
1619
- }
1620
- if (examples.length === 1) {
1621
- return [
1622
- {
1623
- index: 0,
1624
- commonnessScore: 0,
1625
- samplingWeight: this.config.maxWeight,
1626
- ngramStats: {
1627
- totalNgrams: this.extractNgrams(examples[0]).length,
1628
- commonNgrams: 0,
1629
- commonRatio: 0
1630
- }
1631
- }
1632
- ];
1633
- }
1634
- const corpusFrequencies = this.buildCorpusFrequencies(examples);
1635
- const threshold = this.computeThreshold(corpusFrequencies);
1636
- const results = examples.map((example, index) => {
1637
- const ngrams = this.extractNgrams(example);
1638
- const totalNgrams = ngrams.length;
1639
- if (totalNgrams === 0) {
1640
- return {
1641
- index,
1642
- commonnessScore: 0,
1643
- samplingWeight: this.config.maxWeight,
1644
- ngramStats: { totalNgrams: 0, commonNgrams: 0, commonRatio: 0 }
1645
- };
1646
- }
1647
- let commonCount = 0;
1648
- for (const ngram of ngrams) {
1649
- const freq = corpusFrequencies.get(ngram) ?? 0;
1650
- if (freq >= threshold) {
1651
- commonCount++;
1652
- }
1653
- }
1654
- const commonRatio = commonCount / totalNgrams;
1655
- const commonnessScore = commonRatio;
1656
- const samplingWeight = this.commonnessToWeight(commonnessScore);
1657
- return {
1658
- index,
1659
- commonnessScore,
1660
- samplingWeight,
1661
- ngramStats: {
1662
- totalNgrams,
1663
- commonNgrams: commonCount,
1664
- commonRatio
1665
- }
1666
- };
1667
- });
1668
- return results;
1669
- }
1670
- /**
1671
- * Compute aggregate statistics for a set of SoftDedup results.
1672
- */
1673
- computeStats(results) {
1674
- if (results.length === 0) {
1675
- return {
1676
- totalExamples: 0,
1677
- meanWeight: 0,
1678
- medianWeight: 0,
1679
- stdWeight: 0,
1680
- atMinWeight: 0,
1681
- atMaxWeight: 0,
1682
- effectiveDatasetSize: 0,
1683
- reductionRatio: 0,
1684
- uniqueNgramsInCorpus: 0,
1685
- commonThresholdFrequency: 0
1686
- };
1687
- }
1688
- const weights = results.map((r) => r.samplingWeight);
1689
- const totalExamples = results.length;
1690
- const sum = weights.reduce((a, b) => a + b, 0);
1691
- const meanWeight = sum / totalExamples;
1692
- const sorted = [...weights].sort((a, b) => a - b);
1693
- const mid = Math.floor(sorted.length / 2);
1694
- const medianWeight = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
1695
- const variance = weights.reduce((acc, w) => acc + (w - meanWeight) ** 2, 0) / totalExamples;
1696
- const stdWeight = Math.sqrt(variance);
1697
- const epsilon = 1e-9;
1698
- const atMinWeight = weights.filter((w) => Math.abs(w - this.config.minWeight) < epsilon).length;
1699
- const atMaxWeight = weights.filter((w) => Math.abs(w - this.config.maxWeight) < epsilon).length;
1700
- const effectiveDatasetSize = sum;
1701
- const reductionRatio = 1 - effectiveDatasetSize / totalExamples;
1702
- return {
1703
- totalExamples,
1704
- meanWeight,
1705
- medianWeight,
1706
- stdWeight,
1707
- atMinWeight,
1708
- atMaxWeight,
1709
- effectiveDatasetSize,
1710
- reductionRatio,
1711
- uniqueNgramsInCorpus: 0,
1712
- // filled by caller if needed
1713
- commonThresholdFrequency: 0
1714
- // filled by caller if needed
1715
- };
1716
- }
1717
- /**
1718
- * Get the current configuration.
1719
- */
1720
- getConfig() {
1721
- return { ...this.config };
1722
- }
1723
- // ===========================================================================
1724
- // INTERNAL METHODS
1725
- // ===========================================================================
1726
- /**
1727
- * Extract n-grams from a text string.
1728
- * Supports both character-level and word-level n-grams.
1729
- */
1730
- extractNgrams(text) {
1731
- const ngrams = [];
1732
- for (const n of this.config.ngramSizes) {
1733
- if (this.config.wordLevel) {
1734
- const words = text.split(/\s+/).filter((w) => w.length > 0);
1735
- for (let i = 0; i <= words.length - n; i++) {
1736
- ngrams.push(words.slice(i, i + n).join(" "));
1737
- }
1738
- } else {
1739
- const normalized = text.toLowerCase();
1740
- for (let i = 0; i <= normalized.length - n; i++) {
1741
- ngrams.push(normalized.substring(i, i + n));
1742
- }
1743
- }
1744
- }
1745
- return ngrams;
1746
- }
1747
- /**
1748
- * Build a frequency map of all n-grams across the entire corpus.
1749
- */
1750
- buildCorpusFrequencies(examples) {
1751
- const frequencies = /* @__PURE__ */ new Map();
1752
- for (const example of examples) {
1753
- const ngrams = this.extractNgrams(example);
1754
- for (const ngram of ngrams) {
1755
- frequencies.set(ngram, (frequencies.get(ngram) ?? 0) + 1);
1756
- }
1757
- }
1758
- return frequencies;
1759
- }
1760
- /**
1761
- * Compute the frequency threshold above which an n-gram is considered "common".
1762
- * Uses the configured percentile of the frequency distribution.
1763
- */
1764
- computeThreshold(frequencies) {
1765
- if (frequencies.size === 0) {
1766
- return 1;
1767
- }
1768
- const freqValues = Array.from(frequencies.values()).sort((a, b) => a - b);
1769
- const percentileIndex = Math.floor(freqValues.length * this.config.commonThresholdPercentile);
1770
- const clampedIndex = Math.min(percentileIndex, freqValues.length - 1);
1771
- return Math.max(freqValues[clampedIndex], 2);
1772
- }
1773
- /**
1774
- * Convert a commonness score (0-1) to a sampling weight.
1775
- *
1776
- * Uses exponential decay with temperature scaling:
1777
- * weight = maxWeight * exp(-commonnessScore / temperature)
1778
- *
1779
- * Then clamps to [minWeight, maxWeight].
1780
- */
1781
- commonnessToWeight(commonnessScore) {
1782
- const { minWeight, maxWeight, temperature } = this.config;
1783
- const rawWeight = maxWeight * Math.exp(-commonnessScore / temperature);
1784
- return Math.max(minWeight, Math.min(maxWeight, rawWeight));
1785
- }
1786
- /**
1787
- * Validate configuration parameters.
1788
- * @throws Error if configuration is invalid
1789
- */
1790
- validateConfig() {
1791
- const { minWeight, maxWeight, temperature, commonThresholdPercentile, ngramSizes } = this.config;
1792
- if (minWeight <= 0 || minWeight > 1) {
1793
- throw new Error(`SoftDedup: minWeight must be in (0, 1], got ${minWeight}`);
1794
- }
1795
- if (maxWeight < minWeight || maxWeight > 1) {
1796
- throw new Error(`SoftDedup: maxWeight must be in [minWeight, 1], got ${maxWeight}`);
1797
- }
1798
- if (temperature <= 0) {
1799
- throw new Error(`SoftDedup: temperature must be > 0, got ${temperature}`);
1800
- }
1801
- if (commonThresholdPercentile < 0 || commonThresholdPercentile > 1) {
1802
- throw new Error(
1803
- `SoftDedup: commonThresholdPercentile must be in [0, 1], got ${commonThresholdPercentile}`
1804
- );
1805
- }
1806
- if (ngramSizes.length === 0) {
1807
- throw new Error("SoftDedup: ngramSizes must have at least one entry");
1808
- }
1809
- for (const n of ngramSizes) {
1810
- if (n < 1 || !Number.isInteger(n)) {
1811
- throw new Error(`SoftDedup: each ngramSize must be a positive integer, got ${n}`);
1812
- }
1813
- }
1814
- }
1815
- };
1816
- function createSoftDedup(config = {}) {
1817
- return new SoftDedup(config);
1818
- }
1819
-
1820
- // src/training/LRScheduler.ts
1821
- var DEFAULT_LR_SCHEDULER_CONFIG = {
1822
- baseLR: 2e-4,
1823
- totalSteps: 1e3,
1824
- warmupRatio: 0.1,
1825
- minLR: 0,
1826
- numCycles: 1
1827
- };
1828
- var GRPO_LR_SCHEDULER_CONFIG = {
1829
- baseLR: 1e-6,
1830
- totalSteps: 1e3,
1831
- warmupRatio: 0.1,
1832
- minLR: 0,
1833
- numCycles: 1
1834
- };
1835
- var LRScheduler = class {
1836
- config;
1837
- warmupSteps;
1838
- constructor(config = {}) {
1839
- this.config = { ...DEFAULT_LR_SCHEDULER_CONFIG, ...config };
1840
- this.validateConfig();
1841
- this.warmupSteps = Math.floor(this.config.totalSteps * this.config.warmupRatio);
1842
- }
1843
- /**
1844
- * Get the learning rate at a given training step.
1845
- *
1846
- * @param step - Current training step (0-indexed)
1847
- * @returns The learning rate at this step
1848
- */
1849
- getLR(step) {
1850
- const { baseLR, totalSteps, minLR, numCycles } = this.config;
1851
- const clampedStep = Math.max(0, Math.min(step, totalSteps));
1852
- if (clampedStep < this.warmupSteps) {
1853
- if (this.warmupSteps === 0) {
1854
- return baseLR;
1855
- }
1856
- return baseLR * (clampedStep / this.warmupSteps);
1857
- }
1858
- const decaySteps = totalSteps - this.warmupSteps;
1859
- if (decaySteps <= 0) {
1860
- return baseLR;
1861
- }
1862
- const decayProgress = (clampedStep - this.warmupSteps) / decaySteps;
1863
- const cosineArg = Math.PI * numCycles * decayProgress;
1864
- const cosineValue = (1 + Math.cos(cosineArg)) / 2;
1865
- return minLR + (baseLR - minLR) * cosineValue;
1866
- }
1867
- /**
1868
- * Get a detailed snapshot of the scheduler state at a given step.
1869
- *
1870
- * @param step - Current training step (0-indexed)
1871
- * @returns LRSchedulerSnapshot with full state information
1872
- */
1873
- getSnapshot(step) {
1874
- const { totalSteps } = this.config;
1875
- const clampedStep = Math.max(0, Math.min(step, totalSteps));
1876
- const learningRate = this.getLR(step);
1877
- const isWarmup = clampedStep < this.warmupSteps;
1878
- const phase = isWarmup ? "warmup" : "decay";
1879
- let phaseProgress;
1880
- if (isWarmup) {
1881
- phaseProgress = this.warmupSteps === 0 ? 1 : clampedStep / this.warmupSteps;
1882
- } else {
1883
- const decaySteps = totalSteps - this.warmupSteps;
1884
- phaseProgress = decaySteps <= 0 ? 1 : (clampedStep - this.warmupSteps) / decaySteps;
1885
- }
1886
- const overallProgress = totalSteps === 0 ? 1 : clampedStep / totalSteps;
1887
- return {
1888
- step: clampedStep,
1889
- learningRate,
1890
- phase,
1891
- phaseProgress,
1892
- overallProgress
1893
- };
1894
- }
1895
- /**
1896
- * Compute summary statistics for the full LR schedule.
1897
- * Samples every step to compute the average LR.
1898
- *
1899
- * For large totalSteps, this samples at most 10000 evenly-spaced points
1900
- * for efficiency.
1901
- */
1902
- getStats() {
1903
- const { baseLR, minLR, totalSteps } = this.config;
1904
- const decaySteps = totalSteps - this.warmupSteps;
1905
- let sumLR = 0;
1906
- const sampleCount = Math.min(totalSteps + 1, 1e4);
1907
- const stepSize = totalSteps / Math.max(sampleCount - 1, 1);
1908
- for (let i = 0; i < sampleCount; i++) {
1909
- const step = Math.round(i * stepSize);
1910
- sumLR += this.getLR(step);
1911
- }
1912
- const avgLR = sampleCount > 0 ? sumLR / sampleCount : 0;
1913
- return {
1914
- peakLR: baseLR,
1915
- minLR,
1916
- warmupSteps: this.warmupSteps,
1917
- decaySteps: Math.max(0, decaySteps),
1918
- totalSteps,
1919
- avgLR
1920
- };
1921
- }
1922
- /**
1923
- * Generate the full LR schedule as an array of [step, lr] pairs.
1924
- * Useful for plotting or debugging.
1925
- *
1926
- * @param numPoints - Number of points to sample (default: 100)
1927
- * @returns Array of [step, learningRate] tuples
1928
- */
1929
- getSchedule(numPoints = 100) {
1930
- const { totalSteps } = this.config;
1931
- const points = [];
1932
- const clampedPoints = Math.max(2, numPoints);
1933
- for (let i = 0; i < clampedPoints; i++) {
1934
- const step = Math.round(i / (clampedPoints - 1) * totalSteps);
1935
- points.push([step, this.getLR(step)]);
1936
- }
1937
- return points;
1938
- }
1939
- /**
1940
- * Get the number of warmup steps.
1941
- */
1942
- getWarmupSteps() {
1943
- return this.warmupSteps;
1944
- }
1945
- /**
1946
- * Get the current configuration.
1947
- */
1948
- getConfig() {
1949
- return { ...this.config };
1950
- }
1951
- // ===========================================================================
1952
- // INTERNAL METHODS
1953
- // ===========================================================================
1954
- /**
1955
- * Validate configuration parameters.
1956
- * @throws Error if configuration is invalid
1957
- */
1958
- validateConfig() {
1959
- const { baseLR, totalSteps, warmupRatio, minLR, numCycles } = this.config;
1960
- if (baseLR <= 0) {
1961
- throw new Error(`LRScheduler: baseLR must be > 0, got ${baseLR}`);
1962
- }
1963
- if (totalSteps < 0 || !Number.isInteger(totalSteps)) {
1964
- throw new Error(`LRScheduler: totalSteps must be a non-negative integer, got ${totalSteps}`);
1965
- }
1966
- if (warmupRatio < 0 || warmupRatio >= 1) {
1967
- throw new Error(`LRScheduler: warmupRatio must be in [0, 1), got ${warmupRatio}`);
1968
- }
1969
- if (minLR < 0 || minLR >= baseLR) {
1970
- throw new Error(`LRScheduler: minLR must be in [0, baseLR), got ${minLR}`);
1971
- }
1972
- if (numCycles < 1 || !Number.isInteger(numCycles)) {
1973
- throw new Error(`LRScheduler: numCycles must be a positive integer, got ${numCycles}`);
1974
- }
1975
- }
1976
- };
1977
- function createSFTScheduler(config = {}) {
1978
- return new LRScheduler({ ...DEFAULT_LR_SCHEDULER_CONFIG, ...config });
1979
- }
1980
- function createGRPOScheduler(config = {}) {
1981
- return new LRScheduler({ ...GRPO_LR_SCHEDULER_CONFIG, ...config });
1982
- }
1983
-
1984
- // src/training/QualityScoringPipeline.ts
1985
- var DEFAULT_SCORING_CONFIG = {
1986
- syntaxWeight: 0.4,
1987
- schemaWeight: 0.3,
1988
- semanticWeight: 0.3,
1989
- minPassScore: 0.7,
1990
- knownTraits: /* @__PURE__ */ new Set(["Grabbable", "Physics", "Animation", "GaussianSplat", "Tradeable", "NPC"]),
1991
- knownTypes: /* @__PURE__ */ new Set(["orb", "world", "composition", "template"])
1992
- };
1993
- var QualityScoringPipeline = class {
1994
- config;
1995
- constructor(config = {}) {
1996
- this.config = { ...DEFAULT_SCORING_CONFIG, ...config };
1997
- }
1998
- score(source) {
1999
- const details = [];
2000
- const syntax = this.scoreSyntax(source, details);
2001
- const schema = this.scoreSchema(source, details);
2002
- const semantic = this.scoreSemantic(source, details);
2003
- const composite = syntax * this.config.syntaxWeight + schema * this.config.schemaWeight + semantic * this.config.semanticWeight;
2004
- return {
2005
- syntaxValidity: syntax,
2006
- schemaCompliance: schema,
2007
- semanticCorrectness: semantic,
2008
- compositeScore: composite,
2009
- details
2010
- };
2011
- }
2012
- passes(score) {
2013
- return score.compositeScore >= this.config.minPassScore;
2014
- }
2015
- scoreSyntax(source, details) {
2016
- let score = 1;
2017
- if (!source.trim()) {
2018
- details.push({ metric: "syntax", score: 0, message: "Empty source" });
2019
- return 0;
2020
- }
2021
- const open = (source.match(/{/g) || []).length;
2022
- const close = (source.match(/}/g) || []).length;
2023
- if (open !== close) {
2024
- score -= 0.5;
2025
- details.push({
2026
- metric: "syntax",
2027
- score: 0.5,
2028
- message: `Unbalanced braces: ${open} open, ${close} close`
2029
- });
2030
- }
2031
- if (/composition\s+\w+/.test(source) || /orb\s+\w+/.test(source) || /world\s+\w+/.test(source)) {
2032
- details.push({ metric: "syntax", score: 1, message: "Valid composition structure" });
2033
- } else {
2034
- score -= 0.3;
2035
- details.push({
2036
- metric: "syntax",
2037
- score: 0.7,
2038
- message: "No recognized top-level declaration"
2039
- });
2040
- }
2041
- return Math.max(0, score);
2042
- }
2043
- scoreSchema(source, details) {
2044
- let score = 1;
2045
- const traitPattern = /(\w+)\s*{/g;
2046
- let match;
2047
- while ((match = traitPattern.exec(source)) !== null) {
2048
- const name = match[1];
2049
- if (this.config.knownTraits.has(name) || this.config.knownTypes.has(name)) continue;
2050
- if (/^[A-Z]/.test(name) && !this.config.knownTraits.has(name)) {
2051
- score -= 0.1;
2052
- details.push({ metric: "schema", score: 0.9, message: `Unknown trait: ${name}` });
2053
- }
2054
- }
2055
- return Math.max(0, score);
2056
- }
2057
- scoreSemantic(source, details) {
2058
- let score = 1;
2059
- if (/physics\s*{/.test(source) && !/mass|gravity|friction/.test(source)) {
2060
- score -= 0.2;
2061
- details.push({
2062
- metric: "semantic",
2063
- score: 0.8,
2064
- message: "Physics block without mass/gravity/friction"
2065
- });
2066
- }
2067
- if (/animation\s*{/.test(source) && !/clip|duration|speed/.test(source)) {
2068
- score -= 0.2;
2069
- details.push({
2070
- metric: "semantic",
2071
- score: 0.8,
2072
- message: "Animation block without clip/duration"
2073
- });
2074
- }
2075
- if (source.length < 20) {
2076
- score -= 0.3;
2077
- details.push({
2078
- metric: "semantic",
2079
- score: 0.7,
2080
- message: "Source too short for meaningful composition"
2081
- });
2082
- }
2083
- return Math.max(0, score);
2084
- }
2085
- };
2086
-
2087
- // src/training/TrainingPipelineConfig.ts
2088
- var DEFAULT_TRAINING_PIPELINE_CONFIG = {
2089
- qualityScoring: DEFAULT_SCORING_CONFIG,
2090
- softDedup: DEFAULT_SOFTDEDUP_CONFIG,
2091
- lrSchedule: DEFAULT_LR_SCHEDULER_CONFIG,
2092
- hyperparameters: {
2093
- learningRate: 2e-4,
2094
- epochs: 2,
2095
- optimizer: "paged_adamw_8bit",
2096
- microBatchSize: 8,
2097
- gradientAccumulationSteps: 4,
2098
- maxGradNorm: 1,
2099
- weightDecay: 0.01
2100
- },
2101
- pipeline: {
2102
- enableQualityFilter: true,
2103
- enableSoftDedup: true,
2104
- enableLRSchedule: true
2105
- }
2106
- };
2107
- function buildTrainingPipelineConfig(overrides = {}) {
2108
- return {
2109
- qualityScoring: {
2110
- ...DEFAULT_TRAINING_PIPELINE_CONFIG.qualityScoring,
2111
- ...overrides.qualityScoring
2112
- },
2113
- softDedup: {
2114
- ...DEFAULT_TRAINING_PIPELINE_CONFIG.softDedup,
2115
- ...overrides.softDedup
2116
- },
2117
- lrSchedule: {
2118
- ...DEFAULT_TRAINING_PIPELINE_CONFIG.lrSchedule,
2119
- ...overrides.lrSchedule
2120
- },
2121
- hyperparameters: {
2122
- ...DEFAULT_TRAINING_PIPELINE_CONFIG.hyperparameters,
2123
- ...overrides.hyperparameters
2124
- },
2125
- pipeline: {
2126
- ...DEFAULT_TRAINING_PIPELINE_CONFIG.pipeline,
2127
- ...overrides.pipeline
2128
- }
2129
- };
2130
- }
2131
- function computeTotalSteps(datasetSize, config) {
2132
- const { microBatchSize, gradientAccumulationSteps, epochs } = config.hyperparameters;
2133
- const effectiveBatchSize = microBatchSize * gradientAccumulationSteps;
2134
- const stepsPerEpoch = Math.ceil(datasetSize / effectiveBatchSize);
2135
- return stepsPerEpoch * epochs;
2136
- }
2137
-
2138
- // src/training/trainingmonkey/TrainingMonkeyTypes.ts
2139
- var DEFAULT_INTEGRATION_CONFIG = {
2140
- inputPath: "",
2141
- outputDir: "",
2142
- trainRatio: 0.9,
2143
- seed: 42,
2144
- enableSoftDedup: true,
2145
- modelName: "qwen7b",
2146
- stratify: true
2147
- };
2148
-
2149
- // src/training/trainingmonkey/TrainingMonkeyIntegration.ts
2150
- function createSeededRandom(seed) {
2151
- let state = seed | 0;
2152
- return () => {
2153
- state = state + 1831565813 | 0;
2154
- let t = Math.imul(state ^ state >>> 15, 1 | state);
2155
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
2156
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
2157
- };
2158
- }
2159
- var TrainingMonkeyIntegration = class {
2160
- config;
2161
- softDedup;
2162
- constructor(config = {}) {
2163
- this.config = { ...DEFAULT_INTEGRATION_CONFIG, ...config };
2164
- this.softDedup = new SoftDedup({
2165
- ngramSizes: [3, 5, 7],
2166
- wordLevel: false,
2167
- minWeight: 0.1,
2168
- maxWeight: 1,
2169
- temperature: 1,
2170
- commonThresholdPercentile: 0.7
2171
- });
2172
- }
2173
- // ===========================================================================
2174
- // PUBLIC API
2175
- // ===========================================================================
2176
- /**
2177
- * Run the full integration pipeline on raw JSONL content.
2178
- *
2179
- * @param jsonlContent - Raw JSONL string content from the dataset file
2180
- * @returns Complete IntegrationResult with split data, config, and serialized output
2181
- */
2182
- process(jsonlContent) {
2183
- const entries = this.readJsonl(jsonlContent);
2184
- const alpacaEntries = this.convertToAlpaca(entries);
2185
- const weightedEntries = this.config.enableSoftDedup ? this.applySoftDedup(alpacaEntries, entries) : alpacaEntries.map(
2186
- (entry, index) => ({
2187
- ...entry,
2188
- sampling_weight: 1,
2189
- metadata: entries[index]?.metadata
2190
- })
2191
- );
2192
- const split = this.splitDataset(weightedEntries);
2193
- const config = this.generateConfig(split);
2194
- const trainJsonl = this.serializeJsonl(split.train);
2195
- const validationJsonl = this.serializeJsonl(split.validation);
2196
- const configJson = JSON.stringify(config, null, 2);
2197
- return {
2198
- split,
2199
- config,
2200
- trainJsonl,
2201
- validationJsonl,
2202
- configJson
2203
- };
2204
- }
2205
- /**
2206
- * Parse raw JSONL content into SpatialTrainingJSONLEntry objects.
2207
- *
2208
- * @param jsonlContent - Raw JSONL string (one JSON object per line)
2209
- * @returns Array of parsed entries
2210
- * @throws Error if a line contains invalid JSON
2211
- */
2212
- readJsonl(jsonlContent) {
2213
- const lines = jsonlContent.split("\n").filter((line) => line.trim() !== "");
2214
- const entries = [];
2215
- for (let i = 0; i < lines.length; i++) {
2216
- try {
2217
- const parsed = JSON.parse(lines[i]);
2218
- if (!parsed.instruction || !parsed.response) {
2219
- throw new Error(`Missing required fields (instruction/response)`);
2220
- }
2221
- entries.push(parsed);
2222
- } catch (e) {
2223
- throw new Error(`Failed to parse JSONL line ${i + 1}: ${e.message}`);
2224
- }
2225
- }
2226
- return entries;
2227
- }
2228
- /**
2229
- * Convert spatial training entries to Alpaca format.
2230
- *
2231
- * Mapping:
2232
- * instruction -> instruction (question/prompt)
2233
- * input -> extracted HoloScript scene from instruction (if present)
2234
- * output -> response (answer)
2235
- *
2236
- * @param entries - Parsed spatial training entries
2237
- * @returns Alpaca-formatted entries
2238
- */
2239
- convertToAlpaca(entries) {
2240
- return entries.map((entry) => {
2241
- const { questionPart, scenePart } = this.extractSceneFromInstruction(entry.instruction);
2242
- return {
2243
- instruction: questionPart,
2244
- input: scenePart,
2245
- output: entry.response
2246
- };
2247
- });
2248
- }
2249
- /**
2250
- * Apply SoftDedup (W.008) n-gram reweighting to Alpaca entries.
2251
- *
2252
- * Uses the instruction + output text to compute n-gram commonness scores
2253
- * and assigns sampling weights. Template-generated near-duplicates receive
2254
- * lower weights (min 0.1), while unique examples keep weight 1.0.
2255
- *
2256
- * @param alpacaEntries - Converted Alpaca entries
2257
- * @param originalEntries - Original JSONL entries (for metadata preservation)
2258
- * @returns Weighted Alpaca entries with sampling_weight and metadata
2259
- */
2260
- applySoftDedup(alpacaEntries, originalEntries) {
2261
- const textCorpus = alpacaEntries.map((entry) => `${entry.instruction} ${entry.output}`);
2262
- const dedupResults = this.softDedup.process(textCorpus);
2263
- return alpacaEntries.map((entry, index) => ({
2264
- ...entry,
2265
- sampling_weight: dedupResults[index]?.samplingWeight ?? 1,
2266
- metadata: originalEntries[index]?.metadata
2267
- }));
2268
- }
2269
- /**
2270
- * Split weighted entries into train/validation sets.
2271
- *
2272
- * When stratified=true, the split preserves the distribution of
2273
- * relationship_type and difficulty across both sets.
2274
- *
2275
- * @param entries - Weighted Alpaca entries
2276
- * @returns DatasetSplit with train, validation, and stats
2277
- */
2278
- splitDataset(entries) {
2279
- if (entries.length === 0) {
2280
- return {
2281
- train: [],
2282
- validation: [],
2283
- stats: {
2284
- totalExamples: 0,
2285
- trainCount: 0,
2286
- validationCount: 0,
2287
- trainRatio: 0,
2288
- validationRatio: 0,
2289
- stratified: this.config.stratify
2290
- }
2291
- };
2292
- }
2293
- const rng = createSeededRandom(this.config.seed);
2294
- let train;
2295
- let validation;
2296
- if (this.config.stratify && entries[0]?.metadata) {
2297
- ({ train, validation } = this.stratifiedSplit(entries, rng));
2298
- } else {
2299
- ({ train, validation } = this.randomSplit(entries, rng));
2300
- }
2301
- const stats = {
2302
- totalExamples: entries.length,
2303
- trainCount: train.length,
2304
- validationCount: validation.length,
2305
- trainRatio: train.length / entries.length,
2306
- validationRatio: validation.length / entries.length,
2307
- stratified: this.config.stratify && !!entries[0]?.metadata
2308
- };
2309
- return { train, validation, stats };
2310
- }
2311
- /**
2312
- * Generate a TrainingMonkey-compatible training configuration.
2313
- *
2314
- * Uses W.006 hyperparameters:
2315
- * - Learning rate: 2e-4
2316
- * - Epochs: 2
2317
- * - Optimizer: paged_adamw_8bit
2318
- *
2319
- * Uses W.007 batch sizing:
2320
- * - Micro-batch: 8
2321
- * - Gradient accumulation: 4
2322
- * - Effective batch: 32
2323
- *
2324
- * Uses W.009 LR schedule:
2325
- * - Warmup: 10% of total steps
2326
- * - Schedule: cosine decay
2327
- *
2328
- * @param split - The dataset split result
2329
- * @returns TrainingMonkey-compatible config
2330
- */
2331
- generateConfig(split) {
2332
- const microBatchSize = 8;
2333
- const gradientAccumulationSteps = 4;
2334
- const epochs = 2;
2335
- const effectiveBatchSize = microBatchSize * gradientAccumulationSteps;
2336
- const stepsPerEpoch = Math.ceil(split.stats.trainCount / effectiveBatchSize);
2337
- const totalSteps = stepsPerEpoch * epochs;
2338
- const weights = split.train.map((e) => e.sampling_weight);
2339
- const meanWeight = weights.length > 0 ? weights.reduce((sum, w) => sum + w, 0) / weights.length : 1;
2340
- const effectiveSize = weights.reduce((sum, w) => sum + w, 0);
2341
- const reductionRatio = weights.length > 0 ? 1 - effectiveSize / weights.length : 0;
2342
- return {
2343
- model: {
2344
- name: this.config.modelName,
2345
- maxSeqLength: 2048
2346
- },
2347
- hyperparameters: {
2348
- learningRate: 2e-4,
2349
- epochs,
2350
- optimizer: "paged_adamw_8bit",
2351
- microBatchSize,
2352
- gradientAccumulationSteps,
2353
- maxGradNorm: 1,
2354
- weightDecay: 0.01
2355
- },
2356
- lrSchedule: {
2357
- warmupRatio: 0.1,
2358
- type: "cosine"
2359
- },
2360
- dataset: {
2361
- trainPath: `${this.config.outputDir}/alpaca-train.jsonl`,
2362
- validationPath: `${this.config.outputDir}/alpaca-val.jsonl`,
2363
- trainCount: split.stats.trainCount,
2364
- validationCount: split.stats.validationCount,
2365
- totalSteps
2366
- },
2367
- softDedup: {
2368
- applied: this.config.enableSoftDedup,
2369
- meanWeight,
2370
- effectiveSize,
2371
- reductionRatio
2372
- }
2373
- };
2374
- }
2375
- /**
2376
- * Serialize weighted Alpaca entries to JSONL string.
2377
- *
2378
- * @param entries - Entries to serialize
2379
- * @returns JSONL string (one JSON object per line)
2380
- */
2381
- serializeJsonl(entries) {
2382
- return entries.map((entry) => JSON.stringify(entry)).join("\n");
2383
- }
2384
- /**
2385
- * Get the current integration configuration.
2386
- */
2387
- getConfig() {
2388
- return { ...this.config };
2389
- }
2390
- // ===========================================================================
2391
- // INTERNAL METHODS
2392
- // ===========================================================================
2393
- /**
2394
- * Extract the question part and HoloScript scene from an instruction string.
2395
- *
2396
- * The spatial reasoning dataset embeds HoloScript scenes in instructions:
2397
- * ```
2398
- * Does the spatial_adjacent constraint pass?
2399
- *
2400
- * HoloScript Scene:
2401
- * ```holoscript
2402
- * composition "SpatialScene" { ... }
2403
- * ```
2404
- * ```
2405
- *
2406
- * This method splits the instruction into the question (instruction field)
2407
- * and the scene source (input field) for the Alpaca format.
2408
- */
2409
- extractSceneFromInstruction(instruction) {
2410
- const sceneMarkerIndex = instruction.indexOf("HoloScript Scene:");
2411
- if (sceneMarkerIndex === -1) {
2412
- return { questionPart: instruction.trim(), scenePart: "" };
2413
- }
2414
- const questionPart = instruction.substring(0, sceneMarkerIndex).trim();
2415
- const scenePart = instruction.substring(sceneMarkerIndex).trim();
2416
- return { questionPart, scenePart };
2417
- }
2418
- /**
2419
- * Perform a stratified split preserving distribution of metadata fields.
2420
- * Groups by relationship_type + difficulty and splits each group proportionally.
2421
- */
2422
- stratifiedSplit(entries, rng) {
2423
- const groups = /* @__PURE__ */ new Map();
2424
- for (const entry of entries) {
2425
- const key = entry.metadata ? `${entry.metadata.relationship_type}:${entry.metadata.difficulty}` : "unknown";
2426
- if (!groups.has(key)) {
2427
- groups.set(key, []);
2428
- }
2429
- groups.get(key).push(entry);
2430
- }
2431
- const train = [];
2432
- const validation = [];
2433
- for (const [, groupEntries] of groups) {
2434
- const shuffled = this.fisherYatesShuffle([...groupEntries], rng);
2435
- const splitIndex = Math.round(shuffled.length * this.config.trainRatio);
2436
- train.push(...shuffled.slice(0, splitIndex));
2437
- validation.push(...shuffled.slice(splitIndex));
2438
- }
2439
- return { train, validation };
2440
- }
2441
- /**
2442
- * Perform a simple random split.
2443
- */
2444
- randomSplit(entries, rng) {
2445
- const shuffled = this.fisherYatesShuffle([...entries], rng);
2446
- const splitIndex = Math.round(shuffled.length * this.config.trainRatio);
2447
- return {
2448
- train: shuffled.slice(0, splitIndex),
2449
- validation: shuffled.slice(splitIndex)
2450
- };
2451
- }
2452
- /**
2453
- * Fisher-Yates shuffle with seeded PRNG for deterministic ordering.
2454
- */
2455
- fisherYatesShuffle(array, rng) {
2456
- for (let i = array.length - 1; i > 0; i--) {
2457
- const j = Math.floor(rng() * (i + 1));
2458
- [array[i], array[j]] = [array[j], array[i]];
2459
- }
2460
- return array;
2461
- }
2462
- };
2463
- function createTrainingMonkeyIntegration(config = {}) {
2464
- return new TrainingMonkeyIntegration(config);
2465
- }
2466
-
2467
- // src/training/constants.ts
2468
- var TRAINING_CATEGORIES = [
2469
- "vr-interaction",
2470
- "multiplayer",
2471
- "physics",
2472
- "ui-spatial",
2473
- "ai-agents",
2474
- "procedural",
2475
- "audio-spatial",
2476
- "visual-effects",
2477
- "game-mechanics"
2478
- ];
2479
- var DIFFICULTY_LEVELS = ["beginner", "intermediate", "advanced", "production"];
2480
- var QUALITY_THRESHOLDS = {
2481
- Excellent: { min: 90, max: 100 },
2482
- VeryGood: { min: 80, max: 89 },
2483
- Acceptable: { min: 70, max: 79 }
2484
- };
2485
- var DEFAULT_GENERATOR_THRESHOLDS = {
2486
- min_compression_ratio: 5,
2487
- max_compression_ratio: 15,
2488
- max_duplication_rate: 0.05,
2489
- min_templates_per_difficulty: 10,
2490
- min_quality_score: 0.7
2491
- };
2492
- var RULEFORGE_DOMAINS = [
2493
- "ai_agents",
2494
- "physics",
2495
- "robotics",
2496
- "audio",
2497
- "rendering",
2498
- "interaction",
2499
- "multiplayer",
2500
- "vr_ar"
2501
- ];
2502
- function getQualityTier(score) {
2503
- if (score >= QUALITY_THRESHOLDS.Excellent.min) return "Excellent";
2504
- if (score >= QUALITY_THRESHOLDS.VeryGood.min) return "VeryGood";
2505
- if (score >= QUALITY_THRESHOLDS.Acceptable.min) return "Acceptable";
2506
- return "BelowAcceptable";
2507
- }
2508
- function isValidCategory(category) {
2509
- return TRAINING_CATEGORIES.includes(category);
2510
- }
2511
- function isValidDifficulty(difficulty) {
2512
- return DIFFICULTY_LEVELS.includes(difficulty);
2513
- }
2514
-
2515
- // src/training/schema.ts
2516
- function validateTrainingExample(example) {
2517
- const errors = [];
2518
- const warnings = [];
2519
- if (!example || typeof example !== "object") {
2520
- return {
2521
- valid: false,
2522
- errors: [{ field: "root", message: "Example must be an object", severity: "error" }],
2523
- warnings
2524
- };
2525
- }
2526
- const ex = example;
2527
- if (typeof ex.instruction !== "string" || ex.instruction.length === 0) {
2528
- errors.push({
2529
- field: "instruction",
2530
- message: "instruction must be a non-empty string",
2531
- severity: "error"
2532
- });
2533
- }
2534
- if (typeof ex.input !== "string") {
2535
- errors.push({ field: "input", message: "input must be a string", severity: "error" });
2536
- }
2537
- if (typeof ex.output !== "string" || ex.output.length === 0) {
2538
- errors.push({
2539
- field: "output",
2540
- message: "output must be a non-empty string",
2541
- severity: "error"
2542
- });
2543
- }
2544
- if (ex.metadata && typeof ex.metadata === "object") {
2545
- const meta = ex.metadata;
2546
- if (typeof meta.category !== "string") {
2547
- errors.push({
2548
- field: "metadata.category",
2549
- message: "category must be a string",
2550
- severity: "error"
2551
- });
2552
- }
2553
- if (typeof meta.difficulty !== "string") {
2554
- errors.push({
2555
- field: "metadata.difficulty",
2556
- message: "difficulty must be a string",
2557
- severity: "error"
2558
- });
2559
- }
2560
- if (!Array.isArray(meta.traits)) {
2561
- warnings.push("metadata.traits should be an array");
2562
- }
2563
- } else {
2564
- errors.push({ field: "metadata", message: "metadata must be an object", severity: "error" });
2565
- }
2566
- return { valid: errors.length === 0, errors, warnings };
2567
- }
2568
-
2569
- // src/training/trait-mappings.ts
2570
- var TM_REGISTERED_TRAITS = [
2571
- // V43 Tier 1 Core AI
2572
- "llm_agent",
2573
- "behavior_tree",
2574
- "goal_oriented",
2575
- "neural_link",
2576
- "neural_forge",
2577
- "spatial_awareness",
2578
- "shared_world",
2579
- "eye_tracked",
2580
- "hand_tracking",
2581
- "vision",
2582
- // V43 Tier 2 visionOS
2583
- "spatial_persona",
2584
- "shareplay",
2585
- "object_tracking",
2586
- "scene_reconstruction",
2587
- "realitykit_mesh",
2588
- "room_mesh",
2589
- "volumetric_window",
2590
- "spatial_navigation",
2591
- // V43 Tier 2 AI Generative
2592
- "stable_diffusion",
2593
- "controlnet",
2594
- "ai_texture_gen",
2595
- "diffusion_realtime",
2596
- "ai_inpainting",
2597
- "ai_upscaling",
2598
- // V5.1 Hololand Exclusive
2599
- "networked",
2600
- "render_network",
2601
- "openxr_hal",
2602
- "hitl",
2603
- "zora_coins",
2604
- "neural_upscaling",
2605
- "grabbable",
2606
- "throwable",
2607
- "pointable",
2608
- "drawable",
2609
- "attachable",
2610
- "socket",
2611
- "billboard",
2612
- "ui_panel",
2613
- "hud",
2614
- "glowing",
2615
- "physics",
2616
- "persistent",
2617
- "tool",
2618
- // Physics Patterns
2619
- "conversion_tracking",
2620
- "impact_physics",
2621
- "fragment_conversion",
2622
- "damage_falloff",
2623
- "cross_system_integration"
2624
- ];
2625
- function validateTraitName(traitName, validTraits) {
2626
- const normalized = traitName.toLowerCase().replace(/@/g, "").trim();
2627
- const traitSet = validTraits instanceof Set ? validTraits : new Set(validTraits);
2628
- if (traitSet.has(normalized)) return normalized;
2629
- if (traitSet.has(traitName)) return traitName;
2630
- return null;
2631
- }
2632
- function generateValidationReport(tmTraits, hsTraits, deprecatedTraits) {
2633
- const hsSet = hsTraits instanceof Set ? hsTraits : new Set(hsTraits);
2634
- const depSet = deprecatedTraits ?? /* @__PURE__ */ new Set();
2635
- const details = [];
2636
- for (const tm of tmTraits) {
2637
- const normalized = tm.toLowerCase().replace(/@/g, "").trim();
2638
- if (depSet.has(normalized)) {
2639
- details.push({ tmName: tm, hsName: normalized, status: "deprecated" });
2640
- } else if (hsSet.has(normalized)) {
2641
- details.push({ tmName: tm, hsName: normalized, status: "matched" });
2642
- } else {
2643
- details.push({ tmName: tm, hsName: null, status: "unmatched" });
2644
- }
2645
- }
2646
- return {
2647
- matched: details.filter((d) => d.status === "matched").length,
2648
- unmatched: details.filter((d) => d.status === "unmatched").length,
2649
- deprecated: details.filter((d) => d.status === "deprecated").length,
2650
- total: details.length,
2651
- details
2652
- };
2653
- }
2654
- export {
2655
- DEFAULT_GENERATOR_THRESHOLDS,
2656
- DEFAULT_INTEGRATION_CONFIG,
2657
- DEFAULT_LR_SCHEDULER_CONFIG,
2658
- DEFAULT_SCORING_CONFIG,
2659
- DEFAULT_SOFTDEDUP_CONFIG,
2660
- DEFAULT_TRAINING_PIPELINE_CONFIG,
2661
- DIFFICULTY_LEVELS,
2662
- GRPO_LR_SCHEDULER_CONFIG,
2663
- LRScheduler,
2664
- QUALITY_THRESHOLDS,
2665
- QualityScoringPipeline,
2666
- RULEFORGE_DOMAINS,
2667
- SoftDedup,
2668
- SparsityMonitor,
2669
- SpatialTrainingDataGenerator,
2670
- TM_REGISTERED_TRAITS,
2671
- TRAINING_CATEGORIES,
2672
- TrainingMonkeyIntegration,
2673
- buildTrainingPipelineConfig,
2674
- computeTotalSteps,
2675
- createGRPOScheduler,
2676
- createSFTScheduler,
2677
- createSoftDedup,
2678
- createSparsityMonitor,
2679
- createSpatialTrainingDataGenerator,
2680
- createTrainingMonkeyIntegration,
2681
- generateValidationReport,
2682
- getQualityTier,
2683
- isValidCategory,
2684
- isValidDifficulty,
2685
- validateTrainingExample,
2686
- validateTraitName
2687
- };