mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
style: format extracted local services
This commit is contained in:
1 parent
233285fe12
commit
5223b6df5b
33 files changed
+1785
-830
No files matched your search
@@ -1,89 +1,117 @@
|
||||
import { enforceOptInRateLimit, openAiRateLimiter } from './rate-limit.js';
|
||||
import { resolveVoiceModel, isKnownVoiceTier } from '../../../src/voice/voiceCost.js';
|
||||
import { OPENAI_REALTIME_MODEL_MINI_DEFAULT, OPENAI_REALTIME_MODEL_DEFAULT, OPENAI_REALTIME_VOICE_DEFAULT, OPENAI_REALTIME_REASONING_DEFAULT, OPENAI_REALTIME_CONTEXT_TOKENS_DEFAULT, OPENAI_REALTIME_CONTEXT_RETENTION_DEFAULT } from './constants.js';
|
||||
import {
|
||||
resolveVoiceModel,
|
||||
isKnownVoiceTier,
|
||||
} from '../../../src/voice/voiceCost.js';
|
||||
import {
|
||||
OPENAI_REALTIME_MODEL_MINI_DEFAULT,
|
||||
OPENAI_REALTIME_MODEL_DEFAULT,
|
||||
OPENAI_REALTIME_VOICE_DEFAULT,
|
||||
OPENAI_REALTIME_REASONING_DEFAULT,
|
||||
OPENAI_REALTIME_CONTEXT_TOKENS_DEFAULT,
|
||||
OPENAI_REALTIME_CONTEXT_RETENTION_DEFAULT,
|
||||
} from './constants.js';
|
||||
import { realtimeInstructions } from './instructions.js';
|
||||
import { GEV_REALTIME_TOOLS } from './tools.js';
|
||||
|
||||
function createRealtimeTokenHandler({ annotationGuidance } = {}) {
|
||||
return async (req, res) => {
|
||||
if (req.method !== 'GET' && req.method !== 'POST') {
|
||||
res.statusCode = 405;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
||||
return;
|
||||
}
|
||||
return async (req, res) => {
|
||||
if (req.method !== 'GET' && req.method !== 'POST') {
|
||||
res.statusCode = 405;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Opt-in per-IP throttle (GEV_RATELIMIT_OPENAI_PER_MIN). No-op when unset.
|
||||
if (!enforceOptInRateLimit(openAiRateLimiter(), req, res)) return;
|
||||
// Opt-in per-IP throttle (GEV_RATELIMIT_OPENAI_PER_MIN). No-op when unset.
|
||||
if (!enforceOptInRateLimit(openAiRateLimiter(), req, res)) return;
|
||||
|
||||
const apiKey = process.env.OPENAI_API_KEY;
|
||||
if (!apiKey) {
|
||||
res.statusCode = 503;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ error: 'OPENAI_API_KEY is not set' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Voice model tier, requested by the client as ?tier=standard|mini.
|
||||
// resolveVoiceModel is total: an unknown, empty, or hostile value
|
||||
// resolves to `standard` instead of reaching OpenAI as a model id, so a
|
||||
// bad querystring degrades to a normal session rather than a dead mic.
|
||||
// The env overrides stay authoritative per tier (see .env.example) —
|
||||
// a wrong upstream model id is then a config fix, not a code change.
|
||||
const requestedTier = (() => {
|
||||
try {
|
||||
return new URL(req.url || '', 'http://localhost').searchParams.get('tier');
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
const tier = resolveVoiceModel(requestedTier).tier;
|
||||
const model =
|
||||
tier === 'mini'
|
||||
? process.env.OPENAI_REALTIME_MODEL_MINI || OPENAI_REALTIME_MODEL_MINI_DEFAULT
|
||||
: process.env.OPENAI_REALTIME_MODEL || OPENAI_REALTIME_MODEL_DEFAULT;
|
||||
const voice = process.env.OPENAI_REALTIME_VOICE || OPENAI_REALTIME_VOICE_DEFAULT;
|
||||
const effort = process.env.OPENAI_REALTIME_REASONING_EFFORT || OPENAI_REALTIME_REASONING_DEFAULT;
|
||||
const contextTokenLimit = Math.round(Math.max(
|
||||
1000,
|
||||
Math.min(12000, Number(process.env.OPENAI_REALTIME_CONTEXT_TOKENS) || OPENAI_REALTIME_CONTEXT_TOKENS_DEFAULT)
|
||||
));
|
||||
const contextRetentionRatio = Math.max(
|
||||
0.1,
|
||||
Math.min(1, Number(process.env.OPENAI_REALTIME_CONTEXT_RETENTION) || OPENAI_REALTIME_CONTEXT_RETENTION_DEFAULT)
|
||||
);
|
||||
const sessionConfig = {
|
||||
session: {
|
||||
type: 'realtime',
|
||||
model,
|
||||
reasoning: { effort },
|
||||
truncation: {
|
||||
type: 'retention_ratio',
|
||||
retention_ratio: contextRetentionRatio,
|
||||
token_limits: {
|
||||
post_instructions: contextTokenLimit,
|
||||
},
|
||||
},
|
||||
audio: {
|
||||
input: {
|
||||
noise_reduction: { type: 'near_field' },
|
||||
turn_detection: {
|
||||
type: 'semantic_vad',
|
||||
eagerness: 'low',
|
||||
create_response: true,
|
||||
interrupt_response: false,
|
||||
},
|
||||
},
|
||||
output: { voice },
|
||||
},
|
||||
instructions: realtimeInstructions(annotationGuidance),
|
||||
tools: GEV_REALTIME_TOOLS,
|
||||
tool_choice: 'auto',
|
||||
},
|
||||
};
|
||||
const apiKey = process.env.OPENAI_API_KEY;
|
||||
if (!apiKey) {
|
||||
res.statusCode = 503;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ error: 'OPENAI_API_KEY is not set' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Voice model tier, requested by the client as ?tier=standard|mini.
|
||||
// resolveVoiceModel is total: an unknown, empty, or hostile value
|
||||
// resolves to `standard` instead of reaching OpenAI as a model id, so a
|
||||
// bad querystring degrades to a normal session rather than a dead mic.
|
||||
// The env overrides stay authoritative per tier (see .env.example) —
|
||||
// a wrong upstream model id is then a config fix, not a code change.
|
||||
const requestedTier = (() => {
|
||||
try {
|
||||
const response = await fetch('https://api.openai.com/v1/realtime/client_secrets', {
|
||||
return new URL(req.url || '', 'http://localhost').searchParams.get(
|
||||
'tier',
|
||||
);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
const tier = resolveVoiceModel(requestedTier).tier;
|
||||
const model =
|
||||
tier === 'mini'
|
||||
? process.env.OPENAI_REALTIME_MODEL_MINI ||
|
||||
OPENAI_REALTIME_MODEL_MINI_DEFAULT
|
||||
: process.env.OPENAI_REALTIME_MODEL || OPENAI_REALTIME_MODEL_DEFAULT;
|
||||
const voice =
|
||||
process.env.OPENAI_REALTIME_VOICE || OPENAI_REALTIME_VOICE_DEFAULT;
|
||||
const effort =
|
||||
process.env.OPENAI_REALTIME_REASONING_EFFORT ||
|
||||
OPENAI_REALTIME_REASONING_DEFAULT;
|
||||
const contextTokenLimit = Math.round(
|
||||
Math.max(
|
||||
1000,
|
||||
Math.min(
|
||||
12000,
|
||||
Number(process.env.OPENAI_REALTIME_CONTEXT_TOKENS) ||
|
||||
OPENAI_REALTIME_CONTEXT_TOKENS_DEFAULT,
|
||||
),
|
||||
),
|
||||
);
|
||||
const contextRetentionRatio = Math.max(
|
||||
0.1,
|
||||
Math.min(
|
||||
1,
|
||||
Number(process.env.OPENAI_REALTIME_CONTEXT_RETENTION) ||
|
||||
OPENAI_REALTIME_CONTEXT_RETENTION_DEFAULT,
|
||||
),
|
||||
);
|
||||
const sessionConfig = {
|
||||
session: {
|
||||
type: 'realtime',
|
||||
model,
|
||||
reasoning: { effort },
|
||||
truncation: {
|
||||
type: 'retention_ratio',
|
||||
retention_ratio: contextRetentionRatio,
|
||||
token_limits: {
|
||||
post_instructions: contextTokenLimit,
|
||||
},
|
||||
},
|
||||
audio: {
|
||||
input: {
|
||||
noise_reduction: { type: 'near_field' },
|
||||
turn_detection: {
|
||||
type: 'semantic_vad',
|
||||
eagerness: 'low',
|
||||
create_response: true,
|
||||
interrupt_response: false,
|
||||
},
|
||||
},
|
||||
output: { voice },
|
||||
},
|
||||
instructions: realtimeInstructions(annotationGuidance),
|
||||
tools: GEV_REALTIME_TOOLS,
|
||||
tool_choice: 'auto',
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
'https://api.openai.com/v1/realtime/client_secrets',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
@@ -91,26 +119,34 @@ return async (req, res) => {
|
||||
'OpenAI-Safety-Identifier': 'gev-local-dev',
|
||||
},
|
||||
body: JSON.stringify(sessionConfig),
|
||||
});
|
||||
const body = await response.text();
|
||||
res.statusCode = response.status;
|
||||
res.setHeader('Content-Type', response.headers.get('content-type') || 'application/json');
|
||||
// Which tier/model this secret was actually minted for. The upstream
|
||||
// body is passed through untouched (the client parses it verbatim), so
|
||||
// these headers are the authoritative echo — including the case where a
|
||||
// bogus ?tier= was silently downgraded to standard.
|
||||
res.setHeader('X-GEV-Voice-Tier', tier);
|
||||
res.setHeader('X-GEV-Voice-Model', model);
|
||||
if (requestedTier && !isKnownVoiceTier(requestedTier)) {
|
||||
res.setHeader('X-GEV-Voice-Tier-Fallback', '1');
|
||||
}
|
||||
res.end(body);
|
||||
} catch (error) {
|
||||
res.statusCode = 502;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ error: error?.message || 'Failed to create Realtime token' }));
|
||||
},
|
||||
);
|
||||
const body = await response.text();
|
||||
res.statusCode = response.status;
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
response.headers.get('content-type') || 'application/json',
|
||||
);
|
||||
// Which tier/model this secret was actually minted for. The upstream
|
||||
// body is passed through untouched (the client parses it verbatim), so
|
||||
// these headers are the authoritative echo — including the case where a
|
||||
// bogus ?tier= was silently downgraded to standard.
|
||||
res.setHeader('X-GEV-Voice-Tier', tier);
|
||||
res.setHeader('X-GEV-Voice-Model', model);
|
||||
if (requestedTier && !isKnownVoiceTier(requestedTier)) {
|
||||
res.setHeader('X-GEV-Voice-Tier-Fallback', '1');
|
||||
}
|
||||
};
|
||||
res.end(body);
|
||||
} catch (error) {
|
||||
res.statusCode = 502;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: error?.message || 'Failed to create Realtime token',
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { createRealtimeTokenHandler };
|
||||
Reference in new issue
Block a user