mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
refactor: separate local voice handlers and instructions
This commit is contained in:
1 parent
318d37ad8d
commit
638902296a
17 files changed
+1094
-951
No files matched your search
@@ -0,0 +1,90 @@
|
||||
import { keylessHudSummaryResponse } from '../../../src/hudSummaryResponse.js';
|
||||
import { enforceOptInRateLimit, openAiRateLimiter } from './rate-limit.js';
|
||||
import { readRequestBody } from '../common/request.js';
|
||||
import { OPENAI_HUD_SUMMARY_MODEL_DEFAULT } from './constants.js';
|
||||
|
||||
function extractOpenAiResponseText(data) {
|
||||
if (typeof data?.output_text === 'string' && data.output_text.trim()) {
|
||||
return data.output_text.trim();
|
||||
}
|
||||
if (!Array.isArray(data?.output)) return '';
|
||||
return data.output
|
||||
.flatMap((item) => Array.isArray(item?.content) ? item.content : [])
|
||||
.map((part) => part?.text || part?.output_text || '')
|
||||
.join(' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function toFiveWordHudSummary(value) {
|
||||
return String(value || '')
|
||||
.replace(/[^\p{L}\p{N}\s-]/gu, ' ')
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.slice(0, 5)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
async function handleHudSummary(req, res) {
|
||||
if (req.method !== 'POST') {
|
||||
res.statusCode = 405;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const apiKey = process.env.OPENAI_API_KEY;
|
||||
const keyless = keylessHudSummaryResponse(apiKey);
|
||||
if (keyless) {
|
||||
res.statusCode = keyless.statusCode;
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
res.setHeader('Cache-Control', 'no-store');
|
||||
res.end(JSON.stringify(keyless.payload));
|
||||
return;
|
||||
}
|
||||
|
||||
// Opt-in per-IP throttle (GEV_RATELIMIT_OPENAI_PER_MIN). Keyless HUD
|
||||
// fallback has no provider cost and resolves above without consuming a
|
||||
// paid-endpoint quota slot.
|
||||
if (!enforceOptInRateLimit(openAiRateLimiter(), req, res)) return;
|
||||
|
||||
try {
|
||||
const body = await readRequestBody(req, 64 * 1024);
|
||||
const context = JSON.parse(body || '{}');
|
||||
const response = await fetch('https://api.openai.com/v1/responses', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: process.env.OPENAI_HUD_SUMMARY_MODEL || OPENAI_HUD_SUMMARY_MODEL_DEFAULT,
|
||||
instructions: [
|
||||
"Write one concise intelligence-HUD summary for God's Eye View.",
|
||||
'Use only the supplied place, street, nearby-place, and enabled-layer text labels.',
|
||||
'Prefer the clearest named place and include a relevant enabled layer only when useful.',
|
||||
'Do not infer from coordinates or invent a place.',
|
||||
'Output exactly five words with no title, punctuation, markdown, or introductory phrase.',
|
||||
].join(' '),
|
||||
input: JSON.stringify(context),
|
||||
reasoning: { effort: 'minimal' },
|
||||
max_output_tokens: 100,
|
||||
}),
|
||||
});
|
||||
const data = await response.json().catch(() => ({}));
|
||||
const summary = toFiveWordHudSummary(extractOpenAiResponseText(data));
|
||||
res.statusCode = response.ok && summary ? 200 : response.status || 502;
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
res.setHeader('Cache-Control', 'no-store');
|
||||
res.end(JSON.stringify({
|
||||
summary: summary || null,
|
||||
error: response.ok ? null : data.error?.message || 'OpenAI HUD summary request failed',
|
||||
}));
|
||||
} catch (error) {
|
||||
res.statusCode = 502;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ error: error?.message || 'OpenAI HUD summary request failed' }));
|
||||
}
|
||||
}
|
||||
|
||||
export { handleHudSummary };
|
||||
Reference in new issue
Block a user