mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
530 lines
18 KiB
JavaScript
530 lines
18 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
nativeIntegrationFixture,
|
|
assertSuccessfulZoom,
|
|
turnOfEventLoop,
|
|
} from '../testSupport/geminiIntegrationFixture.mjs';
|
|
|
|
const options = { concurrency: false, timeout: 5000 };
|
|
const OLD_TEXT = 'Old packet caption.';
|
|
const OLD_PCM = 'AAABAA==';
|
|
const POST_TOOL_PCM = 'AgADAA==';
|
|
const OLD_CALL = 'old-coalesced-call';
|
|
|
|
function coalescedPacket(callId = OLD_CALL) {
|
|
return {
|
|
serverContent: {
|
|
outputTranscription: { text: OLD_TEXT },
|
|
modelTurn: {
|
|
parts: [
|
|
{ inlineData: { data: OLD_PCM, mimeType: 'audio/pcm;rate=24000' } },
|
|
],
|
|
},
|
|
turnComplete: true,
|
|
},
|
|
toolCall: {
|
|
functionCalls: [
|
|
{
|
|
id: callId,
|
|
name: 'adjust_camera_zoom',
|
|
args: { direction: 'in', amount: 'little' },
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
// Local test observations wrap only injected device fakes and the actual
|
|
// scheduler methods, delegating unchanged. No frozen fixture file is modified.
|
|
function observePacketEffects(h) {
|
|
const played = [];
|
|
const narration = { audible: 0, toolStarted: 0, toolFinished: 0 };
|
|
const createAudio = h.controller.createAudio;
|
|
h.controller.createAudio = (callbacks) => {
|
|
const audio = createAudio(callbacks);
|
|
const play = audio.play.bind(audio);
|
|
audio.play = (data, mimeType) => {
|
|
played.push({ audio, data, mimeType });
|
|
return play(data, mimeType);
|
|
};
|
|
return audio;
|
|
};
|
|
for (const method of Object.keys(narration)) {
|
|
const invoke = h.controller.narration[method].bind(h.controller.narration);
|
|
h.controller.narration[method] = (...args) => {
|
|
narration[method]++;
|
|
return invoke(...args);
|
|
};
|
|
}
|
|
return { played, narration };
|
|
}
|
|
|
|
function packetState(h) {
|
|
const c = h.controller;
|
|
return {
|
|
epoch: c.epoch,
|
|
inputGeneration: c.inputGeneration,
|
|
turn: c.turn,
|
|
completedTurns: c.completedTurns,
|
|
pendingTools: c.pendingTools,
|
|
modelComplete: c.modelComplete,
|
|
pendingCompletionStatus: c.pendingCompletionStatus,
|
|
postToolOutput: c.postToolOutput,
|
|
modelSpoke: c.modelSpoke,
|
|
assistant: c.transcripts.assistant,
|
|
replyPending: c.replyPending,
|
|
retiredReplyPending: c.retiredReplyPending,
|
|
typedText: c.typedIntent?.text ?? null,
|
|
typedResponseId: c.typedIntent?.responseId ?? null,
|
|
narrationTools: c.narration.state().tools,
|
|
narrationTimers: c.narration.state().pendingTimers,
|
|
ownedTimers: h.clock.timers.size,
|
|
};
|
|
}
|
|
|
|
const completions = (h) =>
|
|
h.events.filter((event) => event.type === 'completion');
|
|
const toolEvents = (h, callId) =>
|
|
h.events.filter(
|
|
(event) =>
|
|
['action-call', 'action-result', 'progress'].includes(event.type) &&
|
|
event.callId === callId,
|
|
);
|
|
|
|
function assertOldRemainderAbsent(h, observed) {
|
|
assert.equal(h.state.height, 1000);
|
|
assert.deepEqual(h.state.zooms, []);
|
|
assert.deepEqual(h.runnerCalls, []);
|
|
assert.deepEqual(toolEvents(h, OLD_CALL), []);
|
|
assert.deepEqual(observed.played, []);
|
|
assert.deepEqual(observed.narration, {
|
|
audible: 0,
|
|
toolStarted: 0,
|
|
toolFinished: 0,
|
|
});
|
|
assert.deepEqual(completions(h), []);
|
|
assert.equal(h.controller.completedTurns, 0);
|
|
assert.equal(h.controller.pendingTools, 0);
|
|
assert.equal(
|
|
h.events.filter(
|
|
(event) =>
|
|
event.type === 'transcript' &&
|
|
event.role === 'assistant' &&
|
|
event.final &&
|
|
event.text === OLD_TEXT,
|
|
).length,
|
|
0,
|
|
);
|
|
for (const socket of h.sockets)
|
|
assert.equal(
|
|
h.outputs(socket).some((output) => output.id === OLD_CALL),
|
|
false,
|
|
);
|
|
}
|
|
|
|
for (const mode of ['Stop', 'Stop then Start', 'typed successor']) {
|
|
test(
|
|
`assistant transcript observer ${mode} fences the rest of a coalesced native packet`,
|
|
options,
|
|
async (t) => {
|
|
const h = nativeIntegrationFixture(t);
|
|
const observed = observePacketEffects(h);
|
|
const oldSocket = await h.start();
|
|
const oldAudio = h.audios[0];
|
|
h.activity(oldSocket);
|
|
const before = packetState(h);
|
|
let reached = false;
|
|
let restart;
|
|
let typedAccepted;
|
|
let afterObserver;
|
|
const remove = h.session.subscribe((event) => {
|
|
if (
|
|
reached ||
|
|
event.type !== 'transcript' ||
|
|
event.role !== 'assistant' ||
|
|
event.final ||
|
|
event.text !== OLD_TEXT
|
|
)
|
|
return;
|
|
reached = true;
|
|
if (mode === 'typed successor') {
|
|
typedAccepted = h.session.sendText('Zoom in for the typed successor');
|
|
} else {
|
|
h.session.stop();
|
|
if (mode === 'Stop then Start') restart = h.session.start();
|
|
}
|
|
afterObserver = packetState(h);
|
|
});
|
|
oldSocket.message(coalescedPacket());
|
|
remove();
|
|
assert.equal(
|
|
reached,
|
|
true,
|
|
'the actual common transcript observer ran synchronously',
|
|
);
|
|
// A retired reply may consume its own received terminal, but cannot
|
|
// complete or overwrite the successor. Stop/restart permits no old writes.
|
|
const afterPacketExpected =
|
|
mode === 'typed successor'
|
|
? {
|
|
...afterObserver,
|
|
retiredReplyPending: false,
|
|
ownedTimers: afterObserver.ownedTimers - 1,
|
|
}
|
|
: afterObserver;
|
|
assert.deepEqual(packetState(h), afterPacketExpected);
|
|
await h.controller.toolQueue;
|
|
await turnOfEventLoop();
|
|
if (restart) await restart;
|
|
assertOldRemainderAbsent(h, observed);
|
|
|
|
let successorSocket;
|
|
if (mode === 'typed successor') {
|
|
assert.equal(typedAccepted, true);
|
|
assert.equal(
|
|
h.controller.epoch,
|
|
before.epoch,
|
|
'typed replacement shares the socket epoch',
|
|
);
|
|
assert.ok(h.controller.inputGeneration > before.inputGeneration);
|
|
assert.ok(h.controller.turn > before.turn);
|
|
assert.deepEqual(packetState(h), afterPacketExpected);
|
|
assert.equal(
|
|
h.controller.typedIntent.text,
|
|
'Zoom in for the typed successor',
|
|
);
|
|
assert.equal(h.controller.replyPending, true);
|
|
assert.equal(h.controller.retiredReplyPending, false);
|
|
assert.equal(oldAudio.closed, false);
|
|
const typedMessages = oldSocket.sent.filter((message) =>
|
|
message.clientContent?.turns?.some((turn) =>
|
|
turn.parts?.some(
|
|
(part) => part.text === 'Zoom in for the typed successor',
|
|
),
|
|
),
|
|
);
|
|
assert.equal(typedMessages.length, 1);
|
|
// The received packet already carried the old terminal. No extra
|
|
// provider boundary may be invented before testing the actual successor.
|
|
assert.equal(h.controller.retiredBoundaryWait, null);
|
|
assert.equal(h.controller.cancelledCalls.has(OLD_CALL), true);
|
|
assert.equal(h.controller.replyPending, true);
|
|
assert.equal(h.controller.completedTurns, 0);
|
|
assert.deepEqual(completions(h), []);
|
|
successorSocket = oldSocket;
|
|
} else {
|
|
assert.equal(oldSocket.closed, true);
|
|
assert.equal(oldAudio.closed, true);
|
|
assert.equal(h.controller.narration.state().tools, 0);
|
|
assert.equal(h.controller.narration.state().pendingTimers, 0);
|
|
assert.equal(h.clock.timers.size, 0);
|
|
if (mode === 'Stop') {
|
|
assert.equal(h.session.state, 'idle');
|
|
successorSocket = await h.start();
|
|
} else {
|
|
assert.equal(h.session.state, 'listening');
|
|
assert.equal(h.sockets.length, 2);
|
|
assert.equal(h.audios.length, 2);
|
|
assert.notEqual(h.audios[1], oldAudio);
|
|
assert.equal(h.audios[1].closed, false);
|
|
assert.equal(h.audios[1].pending, false);
|
|
successorSocket = h.sockets[1];
|
|
}
|
|
h.activity(successorSocket);
|
|
}
|
|
await h.tool('owned-after-packet-cancel', successorSocket);
|
|
assertSuccessfulZoom(
|
|
h,
|
|
successorSocket,
|
|
'owned-after-packet-cancel',
|
|
1000,
|
|
);
|
|
assert.equal(h.state.zooms.length, 1);
|
|
assert.equal(h.runnerCalls.length, 1);
|
|
assert.equal(
|
|
h.outputs(successorSocket).some((output) => output.id === OLD_CALL),
|
|
false,
|
|
);
|
|
h.complete(successorSocket);
|
|
assert.equal(completions(h).length, 1);
|
|
assert.equal(h.controller.narration.state().tools, 0);
|
|
assert.equal(h.controller.narration.state().pendingTimers, 0);
|
|
},
|
|
);
|
|
}
|
|
|
|
test(
|
|
'ordinary coalesced packet plays and executes once, then completes only after its post-tool reply',
|
|
options,
|
|
async (t) => {
|
|
const h = nativeIntegrationFixture(t);
|
|
const observed = observePacketEffects(h);
|
|
const ws = await h.start();
|
|
h.activity(ws);
|
|
ws.message(coalescedPacket('ordinary-coalesced'));
|
|
assert.equal(
|
|
h.controller.completedTurns,
|
|
1,
|
|
'first generation terminal is counted once',
|
|
);
|
|
assert.equal(h.controller.pendingTools, 1);
|
|
assert.deepEqual(
|
|
observed.played.map(({ data }) => data),
|
|
[OLD_PCM],
|
|
);
|
|
assert.equal(observed.played[0].audio, h.audios[0]);
|
|
assert.deepEqual(completions(h), [], 'tool and playback are still pending');
|
|
await h.controller.toolQueue;
|
|
assertSuccessfulZoom(h, ws, 'ordinary-coalesced', 1000);
|
|
assert.equal(h.controller.pendingTools, 0);
|
|
assert.equal(observed.narration.toolStarted, 1);
|
|
assert.equal(observed.narration.toolFinished, 1);
|
|
assert.equal(h.controller.narration.state().tools, 0);
|
|
h.audios[0].drain();
|
|
assert.equal(
|
|
h.controller.modelComplete,
|
|
false,
|
|
'function response waits for the post-tool generation',
|
|
);
|
|
assert.equal(h.controller.completedTurns, 1);
|
|
assert.deepEqual(
|
|
completions(h),
|
|
[],
|
|
'draining first-generation PCM cannot finish the tool reply',
|
|
);
|
|
|
|
// The accepted contract has two generations: the function response prompts
|
|
// a fresh model reply. This boundary is intentional, not a stale-packet fix.
|
|
ws.message({
|
|
serverContent: {
|
|
outputTranscription: { text: 'The requested zoom is complete.' },
|
|
modelTurn: {
|
|
parts: [
|
|
{
|
|
inlineData: {
|
|
data: POST_TOOL_PCM,
|
|
mimeType: 'audio/pcm;rate=24000',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
turnComplete: true,
|
|
},
|
|
});
|
|
assert.equal(h.controller.completedTurns, 2);
|
|
assert.deepEqual(
|
|
observed.played.map(({ data }) => data),
|
|
[OLD_PCM, POST_TOOL_PCM],
|
|
);
|
|
assert.deepEqual(
|
|
completions(h),
|
|
[],
|
|
'post-tool playback must drain before completion',
|
|
);
|
|
h.audios[0].drain();
|
|
assert.equal(completions(h).length, 1);
|
|
assert.equal(completions(h)[0].status, 'completed');
|
|
assert.equal(completions(h)[0].spoken, true);
|
|
assert.equal(completions(h)[0].audioDrained, true);
|
|
assert.equal(h.controller.pendingCompletionStatus, null);
|
|
assert.equal(h.controller.narration.state().tools, 0);
|
|
assert.equal(h.controller.narration.state().pendingTimers, 0);
|
|
assert.equal(h.clock.timers.size, 0);
|
|
assert.equal(observed.narration.audible, 2);
|
|
assert.equal(h.runnerCalls.length, 1);
|
|
assert.equal(h.state.zooms.length, 1);
|
|
assert.equal(h.outputs(ws).length, 1);
|
|
h.audios[0].drain();
|
|
assert.equal(
|
|
completions(h).length,
|
|
1,
|
|
'a duplicate device drain cannot complete twice',
|
|
);
|
|
},
|
|
);
|
|
|
|
// Real native message-type fields are separate packets here; no extra old
|
|
// completion is inserted before the accepted successor.
|
|
const BOUNDARY_OLD = 'Prior reply caption.';
|
|
const BOUNDARY_NEXT = 'Zoom in for the typed successor';
|
|
const boundaryZoomCall = (id) => ({
|
|
toolCall: {
|
|
functionCalls: [
|
|
{
|
|
id,
|
|
name: 'adjust_camera_zoom',
|
|
args: { direction: 'in', amount: 'little' },
|
|
},
|
|
],
|
|
},
|
|
});
|
|
function boundarySnapshot(h) {
|
|
const c = h.controller;
|
|
return {
|
|
retiredReplyPending: c.retiredReplyPending,
|
|
retiredBoundaryWait: Boolean(c.retiredBoundaryWait),
|
|
replyPending: c.replyPending,
|
|
typedText: c.typedIntent?.text ?? null,
|
|
completions: h.events.filter((event) => event.type === 'completion').length,
|
|
};
|
|
}
|
|
|
|
for (const mode of [
|
|
'partial assistant transcript',
|
|
'input transcript',
|
|
'interruption event',
|
|
'listening event',
|
|
'final assistant transcript control',
|
|
]) {
|
|
test(
|
|
`received native terminal preserves typed successor after ${mode}`,
|
|
{ concurrency: false, timeout: 5000 },
|
|
async (t) => {
|
|
const h = nativeIntegrationFixture(t);
|
|
const ws = await h.start();
|
|
h.activity(ws);
|
|
let oldDispatch, gate;
|
|
const interruptionMode =
|
|
mode === 'interruption event' || mode === 'listening event';
|
|
if (interruptionMode) {
|
|
h.session.sendText('Prior zoom request');
|
|
gate = h.holdResult('already-committed-old-tool');
|
|
oldDispatch = h.tool('already-committed-old-tool', ws);
|
|
await gate.ready;
|
|
} else if (mode === 'input transcript') {
|
|
ws.message({
|
|
serverContent: {
|
|
outputTranscription: { text: 'Previously emitted output' },
|
|
},
|
|
});
|
|
}
|
|
let entered = 0,
|
|
accepted;
|
|
const remove = h.session.subscribe((event) => {
|
|
if (entered) return;
|
|
const matches =
|
|
mode === 'interruption event'
|
|
? event.type === 'interruption' && event.reason === 'user-speech'
|
|
: mode === 'listening event'
|
|
? event.type === 'state' && event.state === 'listening'
|
|
: mode === 'input transcript'
|
|
? event.type === 'transcript' &&
|
|
event.role === 'user' &&
|
|
event.text === 'Old input caption'
|
|
: event.type === 'transcript' &&
|
|
event.role === 'assistant' &&
|
|
event.text === BOUNDARY_OLD &&
|
|
event.final ===
|
|
(mode === 'final assistant transcript control');
|
|
if (!matches) return;
|
|
entered++;
|
|
accepted = h.session.sendText(BOUNDARY_NEXT);
|
|
});
|
|
if (interruptionMode)
|
|
ws.message({
|
|
serverContent: { interrupted: true, turnComplete: true },
|
|
});
|
|
else
|
|
ws.message({
|
|
serverContent: {
|
|
...(mode === 'input transcript'
|
|
? { inputTranscription: { text: 'Old input caption' } }
|
|
: {}),
|
|
outputTranscription: { text: BOUNDARY_OLD },
|
|
turnComplete: true,
|
|
},
|
|
});
|
|
remove();
|
|
if (oldDispatch) {
|
|
await oldDispatch;
|
|
gate.release();
|
|
}
|
|
await turnOfEventLoop();
|
|
const afterPacket = boundarySnapshot(h);
|
|
const beforeSuccessorHeight = h.state.height;
|
|
// No extra synthetic old boundary. This is the next reply to the accepted text.
|
|
ws.message({
|
|
serverContent: {
|
|
outputTranscription: { text: 'Handling your new zoom.' },
|
|
},
|
|
});
|
|
ws.message(boundaryZoomCall('typed-successor-call'));
|
|
await h.controller.toolQueue;
|
|
await turnOfEventLoop();
|
|
ws.message({ serverContent: { turnComplete: true } });
|
|
const afterSuccessor = boundarySnapshot(h);
|
|
assert.equal(entered, 1);
|
|
assert.equal(accepted, true);
|
|
assert.equal(
|
|
afterPacket.retiredReplyPending,
|
|
false,
|
|
'received old terminal must settle its retirement',
|
|
);
|
|
assert.equal(afterPacket.retiredBoundaryWait, false);
|
|
assert.equal(afterPacket.typedText, BOUNDARY_NEXT);
|
|
assert.equal(
|
|
h.runnerCalls.filter((x) => x.options.callId === 'typed-successor-call')
|
|
.length,
|
|
1,
|
|
'accepted typed successor must reach the actual runner',
|
|
);
|
|
assert.equal(h.state.height, beforeSuccessorHeight * 0.75);
|
|
assert.equal(afterSuccessor.completions, 1);
|
|
assert.equal(afterSuccessor.replyPending, false);
|
|
assert.equal(
|
|
h.controller.cancelledCalls.has('typed-successor-call'),
|
|
false,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
for (const terminalAlreadyApplied of [false, true]) {
|
|
test(
|
|
`typed reentry retains a genuinely pending boundary when terminal already applied is ${terminalAlreadyApplied}`,
|
|
options,
|
|
async (t) => {
|
|
const h = nativeIntegrationFixture(t);
|
|
const ws = await h.start();
|
|
h.activity(ws);
|
|
let entered = false;
|
|
const remove = h.session.subscribe((event) => {
|
|
if (
|
|
entered ||
|
|
event.type !== 'transcript' ||
|
|
event.role !== 'assistant' ||
|
|
event.text !== 'Boundary control' ||
|
|
event.final !== terminalAlreadyApplied
|
|
)
|
|
return;
|
|
entered = true;
|
|
assert.equal(h.session.sendText('First typed replacement'), true);
|
|
if (terminalAlreadyApplied)
|
|
assert.equal(h.session.sendText('Second typed replacement'), true);
|
|
});
|
|
ws.message({
|
|
serverContent: {
|
|
outputTranscription: { text: 'Boundary control' },
|
|
...(terminalAlreadyApplied ? { turnComplete: true } : {}),
|
|
},
|
|
});
|
|
remove();
|
|
assert.equal(entered, true);
|
|
// With no old terminal received, or when that terminal was consumed
|
|
// before the observer, a real predecessor boundary is still outstanding.
|
|
assert.equal(h.controller.retiredReplyPending, true);
|
|
assert.ok(h.controller.retiredBoundaryWait);
|
|
assert.equal(completions(h).length, 0);
|
|
assert.equal(h.runnerCalls.length, 0);
|
|
ws.message({ serverContent: { turnComplete: true } });
|
|
assert.equal(h.controller.retiredReplyPending, false);
|
|
assert.equal(h.controller.retiredBoundaryWait, null);
|
|
await h.tool('after-real-pending-boundary', ws);
|
|
assertSuccessfulZoom(h, ws, 'after-real-pending-boundary', 1000);
|
|
h.complete(ws);
|
|
assert.equal(completions(h).length, 1);
|
|
},
|
|
);
|
|
}
|