@arclabs561/ai-visual-test 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.secretsignore.example +20 -0
- package/CHANGELOG.md +360 -0
- package/CONTRIBUTING.md +63 -0
- package/DEPLOYMENT.md +80 -0
- package/LICENSE +22 -0
- package/README.md +142 -0
- package/SECURITY.md +108 -0
- package/api/health.js +34 -0
- package/api/validate.js +252 -0
- package/index.d.ts +1221 -0
- package/package.json +112 -0
- package/public/index.html +149 -0
- package/src/batch-optimizer.mjs +451 -0
- package/src/bias-detector.mjs +370 -0
- package/src/bias-mitigation.mjs +233 -0
- package/src/cache.mjs +433 -0
- package/src/config.mjs +268 -0
- package/src/constants.mjs +80 -0
- package/src/context-compressor.mjs +350 -0
- package/src/convenience.mjs +617 -0
- package/src/cost-tracker.mjs +257 -0
- package/src/cross-modal-consistency.mjs +170 -0
- package/src/data-extractor.mjs +232 -0
- package/src/dynamic-few-shot.mjs +140 -0
- package/src/dynamic-prompts.mjs +361 -0
- package/src/ensemble/index.mjs +53 -0
- package/src/ensemble-judge.mjs +366 -0
- package/src/error-handler.mjs +67 -0
- package/src/errors.mjs +167 -0
- package/src/experience-propagation.mjs +128 -0
- package/src/experience-tracer.mjs +487 -0
- package/src/explanation-manager.mjs +299 -0
- package/src/feedback-aggregator.mjs +248 -0
- package/src/game-goal-prompts.mjs +478 -0
- package/src/game-player.mjs +548 -0
- package/src/hallucination-detector.mjs +155 -0
- package/src/helpers/playwright.mjs +80 -0
- package/src/human-validation-manager.mjs +516 -0
- package/src/index.mjs +364 -0
- package/src/judge.mjs +929 -0
- package/src/latency-aware-batch-optimizer.mjs +192 -0
- package/src/load-env.mjs +159 -0
- package/src/logger.mjs +55 -0
- package/src/metrics.mjs +187 -0
- package/src/model-tier-selector.mjs +221 -0
- package/src/multi-modal/index.mjs +36 -0
- package/src/multi-modal-fusion.mjs +190 -0
- package/src/multi-modal.mjs +524 -0
- package/src/natural-language-specs.mjs +1071 -0
- package/src/pair-comparison.mjs +277 -0
- package/src/persona/index.mjs +42 -0
- package/src/persona-enhanced.mjs +200 -0
- package/src/persona-experience.mjs +572 -0
- package/src/position-counterbalance.mjs +140 -0
- package/src/prompt-composer.mjs +375 -0
- package/src/render-change-detector.mjs +583 -0
- package/src/research-enhanced-validation.mjs +436 -0
- package/src/retry.mjs +152 -0
- package/src/rubrics.mjs +231 -0
- package/src/score-tracker.mjs +277 -0
- package/src/smart-validator.mjs +447 -0
- package/src/spec-config.mjs +106 -0
- package/src/spec-templates.mjs +347 -0
- package/src/specs/index.mjs +38 -0
- package/src/temporal/index.mjs +102 -0
- package/src/temporal-adaptive.mjs +163 -0
- package/src/temporal-batch-optimizer.mjs +222 -0
- package/src/temporal-constants.mjs +69 -0
- package/src/temporal-context.mjs +49 -0
- package/src/temporal-decision-manager.mjs +271 -0
- package/src/temporal-decision.mjs +669 -0
- package/src/temporal-errors.mjs +58 -0
- package/src/temporal-note-pruner.mjs +173 -0
- package/src/temporal-preprocessor.mjs +543 -0
- package/src/temporal-prompt-formatter.mjs +219 -0
- package/src/temporal-validation.mjs +159 -0
- package/src/temporal.mjs +415 -0
- package/src/type-guards.mjs +311 -0
- package/src/uncertainty-reducer.mjs +470 -0
- package/src/utils/index.mjs +175 -0
- package/src/validation-framework.mjs +321 -0
- package/src/validation-result-normalizer.mjs +64 -0
- package/src/validation.mjs +243 -0
- package/src/validators/accessibility-programmatic.mjs +345 -0
- package/src/validators/accessibility-validator.mjs +223 -0
- package/src/validators/batch-validator.mjs +143 -0
- package/src/validators/hybrid-validator.mjs +268 -0
- package/src/validators/index.mjs +34 -0
- package/src/validators/prompt-builder.mjs +218 -0
- package/src/validators/rubric.mjs +85 -0
- package/src/validators/state-programmatic.mjs +260 -0
- package/src/validators/state-validator.mjs +291 -0
- package/vercel.json +27 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Natural Language Spec Templates
|
|
3
|
+
*
|
|
4
|
+
* Reusable templates for common testing patterns based on real-world BDD usage.
|
|
5
|
+
* Supports template inheritance and composition.
|
|
6
|
+
*
|
|
7
|
+
* Based on research findings:
|
|
8
|
+
* - Real-world BDD patterns (Cucumber, SpecFlow, Behave)
|
|
9
|
+
* - Real-world usage patterns (200+ tests)
|
|
10
|
+
* - Best practices (scenario independence, domain language, living documentation)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { log } from './logger.mjs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Built-in templates for common patterns
|
|
17
|
+
*/
|
|
18
|
+
export const TEMPLATES = {
|
|
19
|
+
/**
|
|
20
|
+
* Game Testing Template
|
|
21
|
+
* Based on real-world interactive game patterns
|
|
22
|
+
*/
|
|
23
|
+
game: {
|
|
24
|
+
name: 'Game Testing',
|
|
25
|
+
description: 'Template for testing interactive games with activation keys',
|
|
26
|
+
spec: `Given I visit {url}
|
|
27
|
+
When I activate the game (press '{activationKey}'{selector})
|
|
28
|
+
Then the game should be playable
|
|
29
|
+
{goals}
|
|
30
|
+
Context: viewport={viewport}, device={device}{temporal}`,
|
|
31
|
+
variables: {
|
|
32
|
+
url: 'game.example.com',
|
|
33
|
+
activationKey: 'g',
|
|
34
|
+
selector: '',
|
|
35
|
+
viewport: '1280x720',
|
|
36
|
+
device: 'desktop',
|
|
37
|
+
goals: '',
|
|
38
|
+
temporal: ''
|
|
39
|
+
},
|
|
40
|
+
examples: [
|
|
41
|
+
{
|
|
42
|
+
name: 'Basic Game',
|
|
43
|
+
values: {
|
|
44
|
+
url: 'example.com',
|
|
45
|
+
activationKey: 'g',
|
|
46
|
+
selector: ', selector: #game-paddle'
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'Game with Temporal',
|
|
51
|
+
values: {
|
|
52
|
+
url: 'game.example.com',
|
|
53
|
+
activationKey: 'g',
|
|
54
|
+
selector: ', selector: #game-element',
|
|
55
|
+
temporal: ', fps: 2, duration: 10 seconds, temporal: true'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Accessibility Template
|
|
63
|
+
*/
|
|
64
|
+
accessibility: {
|
|
65
|
+
name: 'Accessibility Testing',
|
|
66
|
+
description: 'Template for accessibility validation',
|
|
67
|
+
spec: `Given I visit {url}{persona}
|
|
68
|
+
When the page loads
|
|
69
|
+
Then it should be accessible
|
|
70
|
+
{checks}
|
|
71
|
+
Context: viewport={viewport}, device={device}`,
|
|
72
|
+
variables: {
|
|
73
|
+
url: 'example.com',
|
|
74
|
+
persona: '',
|
|
75
|
+
viewport: '1280x720',
|
|
76
|
+
device: 'desktop',
|
|
77
|
+
checks: 'And contrast should meet WCAG standards'
|
|
78
|
+
},
|
|
79
|
+
examples: [
|
|
80
|
+
{
|
|
81
|
+
name: 'Basic Accessibility',
|
|
82
|
+
values: {
|
|
83
|
+
url: 'example.com'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'Accessibility with Persona',
|
|
88
|
+
values: {
|
|
89
|
+
url: 'example.com',
|
|
90
|
+
persona: ' as a visually impaired user'
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Browser Experience Template
|
|
98
|
+
*/
|
|
99
|
+
browser_experience: {
|
|
100
|
+
name: 'Browser Experience',
|
|
101
|
+
description: 'Template for complete user journey testing',
|
|
102
|
+
spec: `Given I visit {url}
|
|
103
|
+
{steps}
|
|
104
|
+
Then {outcome}
|
|
105
|
+
Context: viewport={viewport}, device={device}`,
|
|
106
|
+
variables: {
|
|
107
|
+
url: 'example.com',
|
|
108
|
+
steps: 'When the page loads',
|
|
109
|
+
outcome: 'the page should be usable',
|
|
110
|
+
viewport: '1280x720',
|
|
111
|
+
device: 'desktop'
|
|
112
|
+
},
|
|
113
|
+
examples: [
|
|
114
|
+
{
|
|
115
|
+
name: 'E-commerce Journey',
|
|
116
|
+
values: {
|
|
117
|
+
url: 'shop.example.com',
|
|
118
|
+
steps: `When I browse products
|
|
119
|
+
And I add items to cart
|
|
120
|
+
And I proceed to checkout`,
|
|
121
|
+
outcome: 'the checkout form should be usable'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* State Validation Template
|
|
129
|
+
*/
|
|
130
|
+
state_validation: {
|
|
131
|
+
name: 'State Validation',
|
|
132
|
+
description: 'Template for validating state consistency',
|
|
133
|
+
spec: `Given {initialState}
|
|
134
|
+
When {action}
|
|
135
|
+
Then the state should be consistent
|
|
136
|
+
{checks}
|
|
137
|
+
Context: viewport={viewport}`,
|
|
138
|
+
variables: {
|
|
139
|
+
initialState: 'I visit a page',
|
|
140
|
+
action: 'the state changes',
|
|
141
|
+
checks: 'And the visual representation should match the internal state',
|
|
142
|
+
viewport: '1280x720'
|
|
143
|
+
},
|
|
144
|
+
examples: [
|
|
145
|
+
{
|
|
146
|
+
name: 'Game State',
|
|
147
|
+
values: {
|
|
148
|
+
initialState: 'I play a game',
|
|
149
|
+
action: 'the game state changes',
|
|
150
|
+
checks: 'And cleared elements should be visually removed'
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Temporal Sequence Template
|
|
158
|
+
*/
|
|
159
|
+
temporal: {
|
|
160
|
+
name: 'Temporal Sequence',
|
|
161
|
+
description: 'Template for testing sequences over time',
|
|
162
|
+
spec: `Given {initial}
|
|
163
|
+
When I observe {target} for {duration}
|
|
164
|
+
Then {expected}
|
|
165
|
+
Context: fps={fps}, duration={duration} seconds, temporal: true`,
|
|
166
|
+
variables: {
|
|
167
|
+
initial: 'I visit a page with animations',
|
|
168
|
+
target: 'the page',
|
|
169
|
+
duration: '5',
|
|
170
|
+
fps: '2',
|
|
171
|
+
expected: 'animations should be smooth'
|
|
172
|
+
},
|
|
173
|
+
examples: [
|
|
174
|
+
{
|
|
175
|
+
name: 'Animation Validation',
|
|
176
|
+
values: {
|
|
177
|
+
initial: 'I visit a page with animations',
|
|
178
|
+
target: 'the page',
|
|
179
|
+
duration: '5',
|
|
180
|
+
fps: '2',
|
|
181
|
+
expected: 'animations should be smooth'
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Property-Based Template
|
|
189
|
+
*/
|
|
190
|
+
property: {
|
|
191
|
+
name: 'Property-Based Testing',
|
|
192
|
+
description: 'Template for property/invariant testing',
|
|
193
|
+
spec: 'For all {scope}, {property}',
|
|
194
|
+
variables: {
|
|
195
|
+
scope: 'screenshots',
|
|
196
|
+
property: 'the validation score should be between 0 and 10'
|
|
197
|
+
},
|
|
198
|
+
examples: [
|
|
199
|
+
{
|
|
200
|
+
name: 'Score Range',
|
|
201
|
+
values: {
|
|
202
|
+
scope: 'screenshots',
|
|
203
|
+
property: 'the validation score should be between 0 and 10'
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'State Consistency',
|
|
208
|
+
values: {
|
|
209
|
+
scope: 'game states',
|
|
210
|
+
property: 'the visual representation should match the internal state'
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Create a spec from a template
|
|
219
|
+
*/
|
|
220
|
+
export function createSpecFromTemplate(templateName, variables = {}) {
|
|
221
|
+
const template = TEMPLATES[templateName];
|
|
222
|
+
|
|
223
|
+
if (!template) {
|
|
224
|
+
throw new Error(`Template "${templateName}" not found. Available: ${Object.keys(TEMPLATES).join(', ')}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Merge template variables with provided variables
|
|
228
|
+
const mergedVars = {
|
|
229
|
+
...template.variables,
|
|
230
|
+
...variables
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Replace variables in spec
|
|
234
|
+
let spec = template.spec;
|
|
235
|
+
for (const [key, value] of Object.entries(mergedVars)) {
|
|
236
|
+
const placeholder = `{${key}}`;
|
|
237
|
+
spec = spec.replace(new RegExp(placeholder.replace(/[{}]/g, '\\$&'), 'g'), String(value));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return spec;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Compose multiple templates
|
|
245
|
+
*/
|
|
246
|
+
export function composeTemplates(templates, composition = 'sequential') {
|
|
247
|
+
if (composition === 'sequential') {
|
|
248
|
+
// Sequential: execute one after another
|
|
249
|
+
return templates.map(t => t.spec).join('\n\n');
|
|
250
|
+
} else if (composition === 'parallel') {
|
|
251
|
+
// Parallel: execute all (would need execution framework support)
|
|
252
|
+
return templates.map(t => t.spec).join('\n\n---\n\n');
|
|
253
|
+
} else {
|
|
254
|
+
throw new Error(`Unknown composition type: ${composition}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Inherit from a base template
|
|
260
|
+
*/
|
|
261
|
+
export function inheritTemplate(baseTemplateName, overrides = {}) {
|
|
262
|
+
const base = TEMPLATES[baseTemplateName];
|
|
263
|
+
|
|
264
|
+
if (!base) {
|
|
265
|
+
throw new Error(`Base template "${baseTemplateName}" not found`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
...base,
|
|
270
|
+
...overrides,
|
|
271
|
+
variables: {
|
|
272
|
+
...base.variables,
|
|
273
|
+
...(overrides.variables || {})
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Register a custom template
|
|
280
|
+
*/
|
|
281
|
+
export function registerTemplate(name, template) {
|
|
282
|
+
if (!template.name || !template.spec || !template.variables) {
|
|
283
|
+
throw new Error('Template must have name, spec, and variables');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
TEMPLATES[name] = template;
|
|
287
|
+
log(`[SpecTemplates] Registered custom template: ${name}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* List available templates
|
|
292
|
+
*/
|
|
293
|
+
export function listTemplates() {
|
|
294
|
+
return Object.keys(TEMPLATES).map(name => ({
|
|
295
|
+
name,
|
|
296
|
+
...TEMPLATES[name]
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get template by name
|
|
302
|
+
*/
|
|
303
|
+
export function getTemplate(name) {
|
|
304
|
+
const template = TEMPLATES[name];
|
|
305
|
+
|
|
306
|
+
if (!template) {
|
|
307
|
+
throw new Error(`Template "${name}" not found. Available: ${Object.keys(TEMPLATES).join(', ')}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return template;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Validate template structure
|
|
315
|
+
*/
|
|
316
|
+
export function validateTemplate(template) {
|
|
317
|
+
const errors = [];
|
|
318
|
+
|
|
319
|
+
if (!template.name) {
|
|
320
|
+
errors.push('Template must have a name');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (!template.spec) {
|
|
324
|
+
errors.push('Template must have a spec');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (!template.variables || typeof template.variables !== 'object') {
|
|
328
|
+
errors.push('Template must have variables object');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check that all placeholders in spec have corresponding variables
|
|
332
|
+
const placeholders = template.spec.match(/\{(\w+)\}/g) || [];
|
|
333
|
+
const placeholderNames = placeholders.map(p => p.slice(1, -1));
|
|
334
|
+
const variableNames = Object.keys(template.variables || {});
|
|
335
|
+
|
|
336
|
+
for (const placeholder of placeholderNames) {
|
|
337
|
+
if (!variableNames.includes(placeholder)) {
|
|
338
|
+
errors.push(`Placeholder {${placeholder}} in spec has no corresponding variable`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
valid: errors.length === 0,
|
|
344
|
+
errors
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Natural Language Specs Sub-Module
|
|
3
|
+
*
|
|
4
|
+
* Natural language specification parsing and execution.
|
|
5
|
+
*
|
|
6
|
+
* Import from 'ai-visual-test/specs'
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Core spec functions
|
|
10
|
+
export {
|
|
11
|
+
parseSpec,
|
|
12
|
+
mapToInterfaces,
|
|
13
|
+
executeSpec,
|
|
14
|
+
generatePropertyTests,
|
|
15
|
+
testBehavior,
|
|
16
|
+
validateSpec
|
|
17
|
+
} from '../natural-language-specs.mjs';
|
|
18
|
+
|
|
19
|
+
// Spec templates
|
|
20
|
+
export {
|
|
21
|
+
TEMPLATES,
|
|
22
|
+
createSpecFromTemplate,
|
|
23
|
+
composeTemplates,
|
|
24
|
+
inheritTemplate,
|
|
25
|
+
registerTemplate,
|
|
26
|
+
listTemplates,
|
|
27
|
+
getTemplate,
|
|
28
|
+
validateTemplate
|
|
29
|
+
} from '../spec-templates.mjs';
|
|
30
|
+
|
|
31
|
+
// Spec config
|
|
32
|
+
export {
|
|
33
|
+
createSpecConfig,
|
|
34
|
+
getSpecConfig,
|
|
35
|
+
setSpecConfig,
|
|
36
|
+
resetSpecConfig
|
|
37
|
+
} from '../spec-config.mjs';
|
|
38
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporal Sub-Module
|
|
3
|
+
*
|
|
4
|
+
* All temporal aggregation and decision-making functionality.
|
|
5
|
+
*
|
|
6
|
+
* Import from 'ai-visual-test/temporal'
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Core temporal aggregation
|
|
10
|
+
export {
|
|
11
|
+
aggregateTemporalNotes,
|
|
12
|
+
formatNotesForPrompt,
|
|
13
|
+
calculateCoherenceExported as calculateCoherence
|
|
14
|
+
} from '../temporal.mjs';
|
|
15
|
+
|
|
16
|
+
// Temporal formatting
|
|
17
|
+
export {
|
|
18
|
+
formatTemporalContext,
|
|
19
|
+
formatTemporalForPrompt,
|
|
20
|
+
formatSingleScaleForPrompt,
|
|
21
|
+
formatMultiScaleForPrompt
|
|
22
|
+
} from '../temporal-prompt-formatter.mjs';
|
|
23
|
+
|
|
24
|
+
// Temporal decision management
|
|
25
|
+
export {
|
|
26
|
+
TemporalDecisionManager,
|
|
27
|
+
createTemporalDecisionManager
|
|
28
|
+
} from '../temporal-decision-manager.mjs';
|
|
29
|
+
|
|
30
|
+
// Temporal preprocessing
|
|
31
|
+
export {
|
|
32
|
+
TemporalPreprocessingManager,
|
|
33
|
+
AdaptiveTemporalProcessor,
|
|
34
|
+
createTemporalPreprocessingManager,
|
|
35
|
+
createAdaptiveTemporalProcessor
|
|
36
|
+
} from '../temporal-preprocessor.mjs';
|
|
37
|
+
|
|
38
|
+
// Temporal note pruning
|
|
39
|
+
export {
|
|
40
|
+
pruneTemporalNotes,
|
|
41
|
+
propagateNotes,
|
|
42
|
+
selectTopWeightedNotes
|
|
43
|
+
} from '../temporal-note-pruner.mjs';
|
|
44
|
+
|
|
45
|
+
// Temporal decision functions
|
|
46
|
+
export {
|
|
47
|
+
aggregateMultiScale,
|
|
48
|
+
SequentialDecisionContext,
|
|
49
|
+
humanPerceptionTime,
|
|
50
|
+
calculateAttentionWeight
|
|
51
|
+
} from '../temporal-decision.mjs';
|
|
52
|
+
|
|
53
|
+
// Render change detection
|
|
54
|
+
export {
|
|
55
|
+
detectRenderChanges,
|
|
56
|
+
calculateOptimalFPS,
|
|
57
|
+
detectVisualChanges,
|
|
58
|
+
captureOnRenderChanges,
|
|
59
|
+
captureAdaptiveTemporalScreenshots
|
|
60
|
+
} from '../render-change-detector.mjs';
|
|
61
|
+
|
|
62
|
+
// Adaptive temporal
|
|
63
|
+
export {
|
|
64
|
+
aggregateTemporalNotesAdaptive,
|
|
65
|
+
calculateOptimalWindowSize,
|
|
66
|
+
detectActivityPattern
|
|
67
|
+
} from '../temporal-adaptive.mjs';
|
|
68
|
+
|
|
69
|
+
// Temporal batch optimization
|
|
70
|
+
export { TemporalBatchOptimizer } from '../temporal-batch-optimizer.mjs';
|
|
71
|
+
|
|
72
|
+
// Temporal context
|
|
73
|
+
export {
|
|
74
|
+
createTemporalContext,
|
|
75
|
+
mergeTemporalContext,
|
|
76
|
+
extractTemporalContext
|
|
77
|
+
} from '../temporal-context.mjs';
|
|
78
|
+
|
|
79
|
+
// Temporal constants
|
|
80
|
+
export {
|
|
81
|
+
TIME_SCALES,
|
|
82
|
+
MULTI_SCALE_WINDOWS,
|
|
83
|
+
READING_SPEEDS,
|
|
84
|
+
ATTENTION_MULTIPLIERS,
|
|
85
|
+
COMPLEXITY_MULTIPLIERS,
|
|
86
|
+
CONFIDENCE_THRESHOLDS,
|
|
87
|
+
TIME_BOUNDS,
|
|
88
|
+
CONTENT_THRESHOLDS
|
|
89
|
+
} from '../temporal-constants.mjs';
|
|
90
|
+
|
|
91
|
+
// Temporal errors
|
|
92
|
+
export {
|
|
93
|
+
TemporalError,
|
|
94
|
+
PerceptionTimeError,
|
|
95
|
+
SequentialContextError,
|
|
96
|
+
MultiScaleError,
|
|
97
|
+
TemporalBatchError
|
|
98
|
+
} from '../temporal-errors.mjs';
|
|
99
|
+
|
|
100
|
+
// Temporal screenshots (from multi-modal)
|
|
101
|
+
export { captureTemporalScreenshots } from '../multi-modal.mjs';
|
|
102
|
+
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Temporal Aggregation
|
|
3
|
+
*
|
|
4
|
+
* Implements adaptive window sizing based on note frequency and activity type.
|
|
5
|
+
*
|
|
6
|
+
* Research context:
|
|
7
|
+
* - "Towards Dynamic Theory of Mind: Evaluating LLM Adaptation to Temporal Evolution of Human States"
|
|
8
|
+
* (arXiv:2505.17663) - DynToM benchmark, optimal window sizes vary by activity pattern
|
|
9
|
+
* * We use adaptive window sizing based on frequency (loosely related concept)
|
|
10
|
+
* * We do NOT implement the DynToM benchmark or its specific methods
|
|
11
|
+
* - "The Other Mind: How Language Models Exhibit Human Temporal Cognition" (arXiv:2507.15851)
|
|
12
|
+
* * Paper discusses Weber-Fechner law: logarithmic compression of temporal perception
|
|
13
|
+
* * Paper discusses temporal reference points and hierarchical construction
|
|
14
|
+
* * We use LINEAR frequency-based adjustment, NOT logarithmic compression
|
|
15
|
+
* * We do NOT implement temporal reference points
|
|
16
|
+
*
|
|
17
|
+
* IMPORTANT: This implementation uses LINEAR frequency-based window adjustment, NOT the
|
|
18
|
+
* logarithmic compression (Weber-Fechner law) described in arXiv:2507.15851. We cite
|
|
19
|
+
* the papers for adaptive window concepts, but do NOT implement their specific findings.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { aggregateTemporalNotes } from './temporal.mjs';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Calculate optimal window size based on note frequency
|
|
26
|
+
*
|
|
27
|
+
* @param {import('./index.mjs').TemporalNote[]} notes - Temporal notes
|
|
28
|
+
* @param {{
|
|
29
|
+
* minWindow?: number;
|
|
30
|
+
* maxWindow?: number;
|
|
31
|
+
* defaultWindow?: number;
|
|
32
|
+
* }} [options={}] - Options
|
|
33
|
+
* @returns {number} Optimal window size in milliseconds
|
|
34
|
+
*/
|
|
35
|
+
export function calculateOptimalWindowSize(notes, options = {}) {
|
|
36
|
+
const {
|
|
37
|
+
minWindow = 5000,
|
|
38
|
+
maxWindow = 30000,
|
|
39
|
+
defaultWindow = 10000
|
|
40
|
+
} = options;
|
|
41
|
+
|
|
42
|
+
if (notes.length < 2) {
|
|
43
|
+
return defaultWindow;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Calculate note frequency (notes per second)
|
|
47
|
+
const timeSpan = notes[notes.length - 1].timestamp - notes[0].timestamp;
|
|
48
|
+
if (timeSpan <= 0) {
|
|
49
|
+
return defaultWindow;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const frequency = notes.length / (timeSpan / 1000); // notes per second
|
|
53
|
+
|
|
54
|
+
// Adaptive logic based on frequency
|
|
55
|
+
// High frequency (>2 notes/sec): use smaller windows
|
|
56
|
+
// Low frequency (<0.5 notes/sec): use larger windows
|
|
57
|
+
// Medium frequency: use default window
|
|
58
|
+
|
|
59
|
+
if (frequency > 2) {
|
|
60
|
+
return Math.max(minWindow, defaultWindow * 0.5); // 5s for high frequency
|
|
61
|
+
} else if (frequency < 0.5) {
|
|
62
|
+
return Math.min(maxWindow, defaultWindow * 2); // 20s for low frequency
|
|
63
|
+
} else {
|
|
64
|
+
return defaultWindow; // 10s for medium frequency
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Detect activity pattern from notes
|
|
70
|
+
*
|
|
71
|
+
* @param {import('./index.mjs').TemporalNote[]} notes - Temporal notes
|
|
72
|
+
* @returns {'fastChange' | 'slowChange' | 'consistent' | 'erratic'} Activity pattern
|
|
73
|
+
*/
|
|
74
|
+
export function detectActivityPattern(notes) {
|
|
75
|
+
if (notes.length < 3) {
|
|
76
|
+
return 'consistent';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Calculate change rate
|
|
80
|
+
const timeSpan = notes[notes.length - 1].timestamp - notes[0].timestamp;
|
|
81
|
+
const avgTimeBetween = timeSpan / (notes.length - 1);
|
|
82
|
+
|
|
83
|
+
// Calculate score variance
|
|
84
|
+
const scores = notes
|
|
85
|
+
.map(n => n.gameState?.score || 0)
|
|
86
|
+
.filter(s => typeof s === 'number');
|
|
87
|
+
|
|
88
|
+
if (scores.length < 2) {
|
|
89
|
+
return 'consistent';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const meanScore = scores.reduce((a, b) => a + b, 0) / scores.length;
|
|
93
|
+
const variance = scores.reduce((sum, score) => sum + Math.pow(score - meanScore, 2), 0) / scores.length;
|
|
94
|
+
|
|
95
|
+
// Detect direction changes
|
|
96
|
+
let directionChanges = 0;
|
|
97
|
+
for (let i = 1; i < scores.length; i++) {
|
|
98
|
+
const prev = scores[i - 1];
|
|
99
|
+
const curr = scores[i];
|
|
100
|
+
if ((prev < curr && i > 1 && scores[i - 2] > prev) ||
|
|
101
|
+
(prev > curr && i > 1 && scores[i - 2] < prev)) {
|
|
102
|
+
directionChanges++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Classify pattern
|
|
107
|
+
if (avgTimeBetween < 1000 && variance > meanScore * 0.5) {
|
|
108
|
+
return 'fastChange';
|
|
109
|
+
} else if (avgTimeBetween > 2000 && variance < meanScore * 0.2) {
|
|
110
|
+
return 'slowChange';
|
|
111
|
+
} else if (directionChanges > scores.length * 0.3) {
|
|
112
|
+
return 'erratic';
|
|
113
|
+
} else {
|
|
114
|
+
return 'consistent';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Aggregate temporal notes with adaptive window sizing
|
|
120
|
+
*
|
|
121
|
+
* @param {import('./index.mjs').TemporalNote[]} notes - Temporal notes
|
|
122
|
+
* @param {{
|
|
123
|
+
* adaptive?: boolean;
|
|
124
|
+
* windowSize?: number;
|
|
125
|
+
* decayFactor?: number;
|
|
126
|
+
* coherenceThreshold?: number;
|
|
127
|
+
* }} [options={}] - Aggregation options
|
|
128
|
+
* @returns {import('./index.mjs').AggregatedTemporalNotes} Aggregated temporal notes
|
|
129
|
+
*/
|
|
130
|
+
export function aggregateTemporalNotesAdaptive(notes, options = {}) {
|
|
131
|
+
const {
|
|
132
|
+
adaptive = true,
|
|
133
|
+
windowSize,
|
|
134
|
+
decayFactor = 0.9,
|
|
135
|
+
coherenceThreshold = 0.7
|
|
136
|
+
} = options;
|
|
137
|
+
|
|
138
|
+
let finalWindowSize = windowSize;
|
|
139
|
+
|
|
140
|
+
if (adaptive && !windowSize) {
|
|
141
|
+
// Calculate optimal window size based on note frequency
|
|
142
|
+
finalWindowSize = calculateOptimalWindowSize(notes);
|
|
143
|
+
|
|
144
|
+
// Adjust based on activity pattern
|
|
145
|
+
const pattern = detectActivityPattern(notes);
|
|
146
|
+
if (pattern === 'fastChange') {
|
|
147
|
+
finalWindowSize = Math.min(finalWindowSize, 5000); // Prefer smaller for fast changes
|
|
148
|
+
} else if (pattern === 'slowChange') {
|
|
149
|
+
finalWindowSize = Math.max(finalWindowSize, 20000); // Prefer larger for slow changes
|
|
150
|
+
}
|
|
151
|
+
} else if (!finalWindowSize) {
|
|
152
|
+
finalWindowSize = 10000; // Default
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return aggregateTemporalNotes(notes, {
|
|
156
|
+
windowSize: finalWindowSize,
|
|
157
|
+
decayFactor,
|
|
158
|
+
coherenceThreshold
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|