mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Cut the Google requests the HUD summary makes on its own
The HUD builds place context for its AI summary whenever the camera settles, and each build paid for up to four reverse geocodes plus a Nearby Places search, even when no OpenAI key was configured to use them. - Ask GET /api/openai/hud-summary/status once; with no summary configured, or after an unconfigured reply, make no place lookups for the page. - Reverse-geocode at most two points: the view center and one viewport point. - Reuse a lookup until the view center moves a quarter of the visible width, the camera's distance to it doubles or halves, or the camera crosses a lookup height. The reused points are asked again, so only a timeout, failure or empty answer spends. - Repaint the last AI reply when a small pan leaves the context unchanged. Cached-only hosts and voice context lookups are unchanged.
This commit is contained in:
1 parent
e608fee793
commit
c1f60cb7e7
13 files changed
+855
-15
No files matched your search
+47
-11
@@ -203,7 +203,13 @@ export class IntelHUD {
|
||||
this._dataManagerUnsubscribe = null;
|
||||
this._summaryDirty = true;
|
||||
this._summaryRequest = null;
|
||||
this._summaryUnconfigured = false;
|
||||
// The last place lookup and the view it answered for (see
|
||||
// getBasemapLabelContext), reused until the view changes enough.
|
||||
this._placeLabels = null;
|
||||
this._lastSummarySignature = '';
|
||||
/** The reply painted for `_lastSummarySignature`, when it was accepted. */
|
||||
this._lastSummaryText = null;
|
||||
this._summaryRevision = 0;
|
||||
// One-shot guards so the very first summary lands immediately instead of
|
||||
// waiting for the 15s interval tick: B) swap the "Awaiting telemetry..."
|
||||
@@ -829,6 +835,15 @@ export class IntelHUD {
|
||||
if (this.summaryPolicy.canRequest?.() === false) return;
|
||||
|
||||
const revision = this._summaryRevision;
|
||||
// The place lookups below are paid Google requests made only to feed the
|
||||
// AI summary, so a server without one must not be asked to make them.
|
||||
if (!(await this._summaryAvailable())) {
|
||||
if (revision !== this._summaryRevision) return;
|
||||
this._summaryDirty = false;
|
||||
this._setSummaryText(fallbackText, animate);
|
||||
return;
|
||||
}
|
||||
if (revision !== this._summaryRevision) return;
|
||||
// Every caller invokes this as `void this._updateSummary(...)`, so nothing
|
||||
// owns the returned promise — a rejection escaping from here lands as an
|
||||
// unhandled rejection in the console. Building the context walks the live
|
||||
@@ -851,6 +866,9 @@ export class IntelHUD {
|
||||
const signature = JSON.stringify(context);
|
||||
if (!force && signature === this._lastSummarySignature) {
|
||||
this._summaryDirty = false;
|
||||
// A settle painted the fallback line over the reply this context
|
||||
// already has; reused place labels make that common, so restore it.
|
||||
if (this._lastSummaryText) this._setSummaryText(this._lastSummaryText);
|
||||
return;
|
||||
}
|
||||
if (this._summaryRequest) return;
|
||||
@@ -858,6 +876,7 @@ export class IntelHUD {
|
||||
if (force) this._setSummaryText(fallbackText, false);
|
||||
this._summaryDirty = false;
|
||||
this._lastSummarySignature = signature;
|
||||
this._lastSummaryText = null;
|
||||
const controller = new AbortController();
|
||||
let timedOut = false;
|
||||
const timeout = window.setTimeout(() => {
|
||||
@@ -873,18 +892,16 @@ export class IntelHUD {
|
||||
const data = response.data;
|
||||
if (revision !== this._summaryRevision) return;
|
||||
if (isHudSummaryUnconfigured(response.status, data)) {
|
||||
this._summaryUnconfigured = true;
|
||||
this._setSummaryText(fallbackText, animate);
|
||||
return;
|
||||
}
|
||||
if (!response.ok || !data?.summary) {
|
||||
throw new Error(data?.error || `HTTP ${response.status}`);
|
||||
}
|
||||
this._setSummaryText(
|
||||
hudSummaryMatchesProvenance(data.summary, context.feedProvenance)
|
||||
? data.summary
|
||||
: fallbackText,
|
||||
animate,
|
||||
);
|
||||
if (hudSummaryMatchesProvenance(data.summary, context.feedProvenance))
|
||||
this._lastSummaryText = data.summary;
|
||||
this._setSummaryText(this._lastSummaryText || fallbackText, animate);
|
||||
} catch (error) {
|
||||
// The 5 s timeout is a failure like any other; a move's abort (or
|
||||
// destroy's) is not.
|
||||
@@ -918,12 +935,31 @@ export class IntelHUD {
|
||||
if (el) el.textContent = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* False once the summary service reports no AI summary: by its capability
|
||||
* probe, when it has one, or by a deliberate unconfigured reply. Unknown
|
||||
* stays available, as before.
|
||||
*/
|
||||
async _summaryAvailable() {
|
||||
if (this._summaryUnconfigured) return false;
|
||||
try {
|
||||
if ((await this.summaryService?.configured?.()) === false)
|
||||
this._summaryUnconfigured = true;
|
||||
} catch {
|
||||
/* an unanswered probe leaves the summary as it was */
|
||||
}
|
||||
return !this._summaryUnconfigured;
|
||||
}
|
||||
|
||||
async _summaryContext() {
|
||||
const labels = await getBasemapLabelContext(
|
||||
this.viewer,
|
||||
this.placeSearch,
|
||||
this.basemapContext,
|
||||
);
|
||||
const revision = this._summaryRevision;
|
||||
const labels = await getBasemapLabelContext(this.viewer, this.placeSearch, {
|
||||
...this.basemapContext,
|
||||
previous: this._placeLabels,
|
||||
});
|
||||
// A slower build for a view the camera has left must not replace the
|
||||
// newer view's lookup.
|
||||
if (revision === this._summaryRevision) this._placeLabels = labels;
|
||||
const enabledLayers =
|
||||
this._dataManager
|
||||
?.getAll?.()
|
||||
|
||||
Reference in new issue
Block a user