mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +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
+71
-1
@@ -154,6 +154,69 @@ export function resolveVoiceModelById(modelId) {
|
||||
};
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ *
|
||||
* INPUT TRANSCRIPTION (voice-card captions)
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* ⚠️ VERIFY AT RELEASE — external prices, USD per 1M tokens, same shape as
|
||||
* the realtime rate tables. Transcription is billed separately from the
|
||||
* session's responses and arrives as `usage` on
|
||||
* `conversation.item.input_audio_transcription.completed`. An unknown model
|
||||
* bills at the most expensive entry, as for session models.
|
||||
*/
|
||||
const transcriptionTable = ({ textInput, audioInput, textOutput }) =>
|
||||
Object.freeze({
|
||||
textInput,
|
||||
audioInput,
|
||||
textOutput,
|
||||
// Transcription output is text; usage without output details would
|
||||
// otherwise be read as audio and priced at zero. Cached input is priced
|
||||
// as uncached so the meter never under-counts.
|
||||
audioOutput: textOutput,
|
||||
textCachedInput: textInput,
|
||||
audioCachedInput: audioInput,
|
||||
});
|
||||
|
||||
export const TRANSCRIPTION_MODEL_RATES = Object.freeze({
|
||||
'gpt-4o-mini-transcribe': transcriptionTable({
|
||||
textInput: 1.25,
|
||||
audioInput: 3,
|
||||
textOutput: 5,
|
||||
}),
|
||||
'gpt-4o-transcribe': transcriptionTable({
|
||||
textInput: 2.5,
|
||||
audioInput: 6,
|
||||
textOutput: 10,
|
||||
}),
|
||||
});
|
||||
|
||||
/** Per-second rate for duration-billed transcription (whisper-style). */
|
||||
export const TRANSCRIPTION_USD_PER_SECOND = 0.006 / 60;
|
||||
|
||||
/** Rate table for a transcription model; unknown ids bill at the priciest. */
|
||||
export function transcriptionRates(modelId) {
|
||||
const id = typeof modelId === 'string' ? modelId.trim() : '';
|
||||
if (Object.prototype.hasOwnProperty.call(TRANSCRIPTION_MODEL_RATES, id))
|
||||
return TRANSCRIPTION_MODEL_RATES[id];
|
||||
return Object.values(TRANSCRIPTION_MODEL_RATES).reduce((worst, rates) =>
|
||||
rates.audioInput > worst.audioInput ? rates : worst,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* USD for one transcription's usage: token usage priced with the model's
|
||||
* rates, or duration usage priced per second.
|
||||
*/
|
||||
export function estimateTranscriptionCostUsd(usage, modelId) {
|
||||
if (!usage) return 0;
|
||||
if (usage.type === 'duration') {
|
||||
const usd = nonNegative(usage.seconds) * TRANSCRIPTION_USD_PER_SECOND;
|
||||
return Number.isFinite(usd) ? usd : 0;
|
||||
}
|
||||
return estimateUsageCostUsd(usage, transcriptionRates(modelId));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ *
|
||||
* SPEND GUARD CONFIG
|
||||
* ------------------------------------------------------------------ */
|
||||
@@ -429,7 +492,14 @@ export function createVoiceCostTracker(options = {}) {
|
||||
* @param {object} usage - `response.usage`
|
||||
*/
|
||||
record(usage) {
|
||||
const usd = estimateUsageCostUsd(usage, model.rates);
|
||||
return this.recordUsd(estimateUsageCostUsd(usage, model.rates));
|
||||
},
|
||||
/**
|
||||
* Fold an already-priced cost (e.g. input transcription) into the same
|
||||
* session total, so the warning and the cap cover it.
|
||||
* @param {number} usd
|
||||
*/
|
||||
recordUsd(usd) {
|
||||
if (usd > 0) {
|
||||
totalUsd += usd;
|
||||
responses += 1;
|
||||
|
||||
Reference in new issue
Block a user