mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
feat(voice): voice core — honest layer reach, spoken summary with on-screen card, narration, point-and-ask
- Voice layer manifest (src/voice/layerManifest.js) generates the layer enums, aliases and analyst field hints; a test fails when a catalog layer has no entry. - analyst_query validates fields, operators, units, values and scopes, counts up to 250,000 loaded records per layer, reports complete/partial/unanswered, tells LAYER_OFF / NOT_READY / FEED_UNAVAILABLE apart from zero, refuses mismatched follow-ups and never lets a superseded query replace follow-up memory. Seven more layers expose analyst records. - Tool results carry a code-built say line and a display card; the voice card in the mic control shows captions, plan steps and the result (src/voice/resultDisplay.js is the one adapter). Slow tools narrate their current step out of band; holding Space while the assistant talks cuts it off. Turn spans go to the debug log; caption transcription is metered; the log omits what was said unless GEV_VOICE_LOG_CONTENT=1. - Point and ask: the Space-down pointer snapshot resolves "this", "that one" and "around here" through 'pointer' targets, and "the second one" through referent:n from the last numbered list. - Prompt states each policy once (instructions + tools 48.5k -> 46.7k chars). - Voice benchmark harness (scripts/qa-voice-bench.mjs, scripts/voice-bench/) shares its phrase table and grading with scripts/qa-voice-routing.mjs.
This commit is contained in:
1 parent
e7707d9a0f
commit
a19897ad15
97 files changed
+14343
-755
No files matched your search
@@ -0,0 +1,225 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createControls as bikeshareControls } from '../layers/bikeshare/controls.js';
|
||||
import { createControls as launchControls } from '../layers/launches/controls.js';
|
||||
import { createControls as installationControls } from '../layers/installations/controls.js';
|
||||
import { createQueries as transitQueries } from '../layers/transit/queries.js';
|
||||
import { createCyclonesLayer } from '../layers/cyclones/index.js';
|
||||
import { ANALYST_LAYERS } from '../data/analystEngine.js';
|
||||
|
||||
/** Every declared analyst field must be a key the accessor actually emits. */
|
||||
function assertDeclaredFields(layerId, record) {
|
||||
const layer = ANALYST_LAYERS[layerId];
|
||||
for (const field of [
|
||||
...layer.numeric,
|
||||
...layer.text,
|
||||
...layer.flags,
|
||||
...layer.time,
|
||||
])
|
||||
assert.ok(Object.hasOwn(record, field), `${layerId} record lacks ${field}`);
|
||||
assert.equal(typeof record.lat, 'number');
|
||||
assert.equal(typeof record.lon, 'number');
|
||||
}
|
||||
|
||||
test('bikeshare stations join coordinates from station information', () => {
|
||||
const state = {
|
||||
_enabled: true,
|
||||
_stationRenderMap: new Map([
|
||||
[
|
||||
'austin:1',
|
||||
{
|
||||
cityId: 'austin',
|
||||
stationId: '1',
|
||||
stationName: 'Congress',
|
||||
bikesAvailable: 4,
|
||||
docksAvailable: 6,
|
||||
capacity: 10,
|
||||
isRenting: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'austin:2',
|
||||
{
|
||||
cityId: 'austin',
|
||||
stationId: '2',
|
||||
stationName: 'No coordinates',
|
||||
bikesAvailable: 1,
|
||||
},
|
||||
],
|
||||
]),
|
||||
_stationInfoCache: new Map([
|
||||
['austin', new Map([['1', { lat: 30.27, lon: -97.74 }]])],
|
||||
]),
|
||||
};
|
||||
const { methods } = bikeshareControls({ state, parts: {} });
|
||||
const rows = methods.getAnalystRecords();
|
||||
assert.equal(
|
||||
rows.length,
|
||||
1,
|
||||
'a station without coordinates is not countable',
|
||||
);
|
||||
assert.deepEqual(rows[0], {
|
||||
id: 'Congress',
|
||||
name: 'Congress',
|
||||
city: 'austin',
|
||||
lat: 30.27,
|
||||
lon: -97.74,
|
||||
bikesAvailable: 4,
|
||||
docksAvailable: 6,
|
||||
capacity: 10,
|
||||
renting: true,
|
||||
});
|
||||
assertDeclaredFields('bikeshare', rows[0]);
|
||||
state._enabled = false;
|
||||
assert.deepEqual(methods.getAnalystRecords(), []);
|
||||
});
|
||||
|
||||
test('launches carry time and hours until launch', () => {
|
||||
const at = Date.now() + 5 * 3_600_000;
|
||||
const state = {
|
||||
_enabled: true,
|
||||
_launches: [
|
||||
{
|
||||
id: 'L1',
|
||||
name: 'Falcon 9 | Starlink',
|
||||
provider: 'SpaceX',
|
||||
status: 'Go',
|
||||
launchSite: 'SLC-40',
|
||||
missionName: 'Starlink',
|
||||
lat: 28.56,
|
||||
lon: -80.58,
|
||||
launchTime: new Date(at).toISOString(),
|
||||
},
|
||||
],
|
||||
};
|
||||
const { methods } = launchControls({ state, parts: {} });
|
||||
const [row] = methods.getAnalystRecords();
|
||||
assert.equal(row.launchTimeMs, at);
|
||||
assert.equal(row.hoursUntil, 5);
|
||||
assertDeclaredFields('rocket-launches', row);
|
||||
});
|
||||
|
||||
test('mapped installations flatten latitude/longitude', () => {
|
||||
const state = {
|
||||
enabled: true,
|
||||
records: [
|
||||
{
|
||||
id: 'node/1',
|
||||
name: 'Fort Example',
|
||||
class: 'military_land',
|
||||
kind: 'installation',
|
||||
latitude: 31,
|
||||
longitude: -97,
|
||||
},
|
||||
],
|
||||
};
|
||||
const { methods } = installationControls({ state, parts: {} });
|
||||
const [row] = methods.getAnalystRecords();
|
||||
assert.deepEqual(row, {
|
||||
id: 'Fort Example',
|
||||
name: 'Fort Example',
|
||||
class: 'military_land',
|
||||
kind: 'installation',
|
||||
lat: 31,
|
||||
lon: -97,
|
||||
});
|
||||
assertDeclaredFields('military-installations', row);
|
||||
});
|
||||
|
||||
test('transit vehicles report their last fix, mode and route', () => {
|
||||
const state = {
|
||||
_enabled: true,
|
||||
_vehicles: new Map([
|
||||
[
|
||||
'cap:1',
|
||||
{
|
||||
key: 'cap:1',
|
||||
mode: 'bus',
|
||||
courseDeg: 90,
|
||||
record: {
|
||||
id: '1',
|
||||
label: '2554',
|
||||
routeId: '335',
|
||||
lat: 30.2,
|
||||
lon: -97.7,
|
||||
speedMps: 8,
|
||||
status: 'IN_TRANSIT_TO',
|
||||
occupancy: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
]),
|
||||
};
|
||||
const { methods } = transitQueries({ state, parts: {} });
|
||||
const [row] = methods.getAnalystRecords();
|
||||
assert.equal(row.id, '2554');
|
||||
assert.equal(row.mode, 'bus');
|
||||
assertDeclaredFields('transit', row);
|
||||
});
|
||||
|
||||
test('cyclones flatten the storm position and stay empty while off', async () => {
|
||||
const storm = {
|
||||
id: 'al052026',
|
||||
name: 'Fay',
|
||||
classification: 'TS',
|
||||
basin: 'AL',
|
||||
position: { latitude: 29.8, longitude: -40 },
|
||||
windKt: 35,
|
||||
pressureHpa: 1010,
|
||||
issuedAt: '2026-09-23T00:00:00Z',
|
||||
forecastPoints: [],
|
||||
track: null,
|
||||
cone: null,
|
||||
};
|
||||
const layer = createCyclonesLayer({
|
||||
feed: {
|
||||
getSnapshot: async () => ({
|
||||
schemaVersion: 1,
|
||||
storms: [storm],
|
||||
unavailable: false,
|
||||
stale: false,
|
||||
fetchedAt: Date.now(),
|
||||
}),
|
||||
},
|
||||
cesium: {
|
||||
ScreenSpaceEventType: { LEFT_CLICK: 'left' },
|
||||
ScreenSpaceEventHandler: class {
|
||||
setInputAction() {}
|
||||
destroy() {}
|
||||
isDestroyed() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
createRendering: () => ({
|
||||
setSnapshot: async () => true,
|
||||
setSelection() {},
|
||||
clear() {},
|
||||
destroy() {},
|
||||
pickStorm: () => null,
|
||||
ownsPickId: () => false,
|
||||
getDiagnostics: () => ({}),
|
||||
}),
|
||||
hitTestOverlay: () => null,
|
||||
});
|
||||
layer.init({
|
||||
scene: { canvas: new EventTarget(), pick: () => null },
|
||||
camera: {},
|
||||
});
|
||||
assert.deepEqual(layer.getAnalystRecords(), []);
|
||||
layer.enable();
|
||||
await layer.update();
|
||||
const [row] = layer.getAnalystRecords();
|
||||
assert.deepEqual(row, {
|
||||
id: 'Fay',
|
||||
name: 'Fay',
|
||||
classification: 'TS',
|
||||
basin: 'AL',
|
||||
lat: 29.8,
|
||||
lon: -40,
|
||||
windKt: 35,
|
||||
pressureHpa: 1010,
|
||||
});
|
||||
assertDeclaredFields('weather-cyclones', row);
|
||||
layer.destroy?.();
|
||||
});
|
||||
Reference in new issue
Block a user