Zhen Ye commited on
Commit
56e4f43
·
1 Parent(s): 87cfe90

Refactor GPT sync in client.js: extract _syncGptFromDetections and add final sync on job completion

Browse files
Files changed (1) hide show
  1. frontend/js/api/client.js +84 -64
frontend/js/api/client.js CHANGED
@@ -90,10 +90,85 @@ APP.api.client.cancelBackendJob = async function (jobId, reason) {
90
  }
91
  };
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  APP.api.client.pollAsyncJob = async function () {
94
  const { state } = APP.core;
95
  const { log, setHfStatus } = APP.ui.logging;
96
  const { fetchProcessedVideo, fetchDepthVideo, fetchDepthFirstFrame } = APP.core.video;
 
97
 
98
  const pollInterval = 3000; // 3 seconds
99
  const maxAttempts = 200; // 10 minutes max
@@ -128,6 +203,14 @@ APP.api.client.pollAsyncJob = async function () {
128
  log(`✓ Backend job ${completedJobId.substring(0, 8)}: completed successfully`, "g");
129
  setHfStatus("job completed, fetching video...");
130
 
 
 
 
 
 
 
 
 
131
  try {
132
  await fetchProcessedVideo();
133
  await fetchDepthVideo();
@@ -161,70 +244,7 @@ APP.api.client.pollAsyncJob = async function () {
161
 
162
  // Check if GPT enrichment has updated first-frame detections
163
  if (status.first_frame_detections && status.first_frame_detections.length > 0) {
164
- const rawDets = status.first_frame_detections;
165
- let needsRender = false;
166
-
167
- // Phase A: Always sync assessment status fields (not gated on gpt_raw)
168
- for (const rd of rawDets) {
169
- const tid = rd.track_id || `T${String(rawDets.indexOf(rd) + 1).padStart(2, "0")}`;
170
- const existing = (state.detections || []).find(d => d.id === tid);
171
- if (existing) {
172
- if (rd.assessment_status && existing.assessment_status !== rd.assessment_status) {
173
- existing.assessment_status = rd.assessment_status;
174
- needsRender = true;
175
- }
176
- if (rd.mission_relevant !== undefined && rd.mission_relevant !== null) {
177
- existing.mission_relevant = rd.mission_relevant;
178
- }
179
- if (rd.relevance_reason) {
180
- existing.relevance_reason = rd.relevance_reason;
181
- }
182
- }
183
- }
184
-
185
- // Phase B: One-shot GPT feature merge (gated on gpt_raw + _gptEnriched flag)
186
- const hasGptData = rawDets.some(d => d.gpt_raw);
187
- if (hasGptData) {
188
- state.hf.firstFrameDetections = rawDets;
189
- for (const rd of rawDets) {
190
- const tid = rd.track_id || `T${String(rawDets.indexOf(rd) + 1).padStart(2, "0")}`;
191
- const existing = (state.detections || []).find(d => d.id === tid);
192
- if (existing && rd.gpt_raw) {
193
- const g = rd.gpt_raw;
194
- const rangeStr = g.range_estimate && g.range_estimate !== "Unknown"
195
- ? g.range_estimate + " (est.)" : "Unknown";
196
- existing.features = {
197
- "Type": g.object_type || "Unknown",
198
- "Size": g.size || "Unknown",
199
- "Threat Lvl": (g.threat_level || g.threat_level_score || "?") + "/10",
200
- "Status": g.threat_classification || "?",
201
- "Weapons": (g.visible_weapons || []).join(", ") || "None Visible",
202
- "Readiness": g.weapon_readiness || "Unknown",
203
- "Motion": g.motion_status || "Unknown",
204
- "Range": rangeStr,
205
- "Bearing": g.bearing || "Unknown",
206
- "Intent": g.tactical_intent || "Unknown",
207
- };
208
- const dynFeats = g.dynamic_features || [];
209
- for (const feat of dynFeats) {
210
- if (feat && feat.key && feat.value) {
211
- existing.features[feat.key] = feat.value;
212
- }
213
- }
214
- existing.threat_level_score = rd.threat_level_score || g.threat_level_score || 0;
215
- existing.threat_classification = rd.threat_classification || g.threat_classification || "Unknown";
216
- existing.weapon_readiness = rd.weapon_readiness || g.weapon_readiness || "Unknown";
217
- existing.gpt_distance_m = rd.gpt_distance_m || null;
218
- existing.gpt_direction = rd.gpt_direction || null;
219
- needsRender = true;
220
- }
221
- }
222
- log("Track cards updated with GPT assessment", "g");
223
- }
224
-
225
- if (needsRender && APP.ui && APP.ui.cards && APP.ui.cards.renderFrameTrackList) {
226
- APP.ui.cards.renderFrameTrackList();
227
- }
228
  }
229
  }
230
 
 
90
  }
91
  };
92
 
93
+ /**
94
+ * Sync GPT enrichment data from polled first_frame_detections into state.detections.
95
+ * Returns true if any card was updated and needs re-render.
96
+ */
97
+ APP.api.client._syncGptFromDetections = function (rawDets, logLabel) {
98
+ const { state } = APP.core;
99
+ const { log } = APP.ui.logging;
100
+ let needsRender = false;
101
+
102
+ // Phase A: Sync assessment status, relevance fields
103
+ for (const rd of rawDets) {
104
+ const tid = rd.track_id || `T${String(rawDets.indexOf(rd) + 1).padStart(2, "0")}`;
105
+ const existing = (state.detections || []).find(d => d.id === tid);
106
+ if (existing) {
107
+ if (rd.assessment_status && existing.assessment_status !== rd.assessment_status) {
108
+ existing.assessment_status = rd.assessment_status;
109
+ needsRender = true;
110
+ }
111
+ if (rd.mission_relevant !== undefined && rd.mission_relevant !== null) {
112
+ existing.mission_relevant = rd.mission_relevant;
113
+ }
114
+ if (rd.relevance_reason) {
115
+ existing.relevance_reason = rd.relevance_reason;
116
+ }
117
+ }
118
+ }
119
+
120
+ // Phase B: Full GPT feature merge (gated on gpt_raw)
121
+ const hasGptData = rawDets.some(d => d.gpt_raw);
122
+ if (hasGptData) {
123
+ state.hf.firstFrameDetections = rawDets;
124
+ for (const rd of rawDets) {
125
+ const tid = rd.track_id || `T${String(rawDets.indexOf(rd) + 1).padStart(2, "0")}`;
126
+ const existing = (state.detections || []).find(d => d.id === tid);
127
+ if (existing && rd.gpt_raw) {
128
+ const g = rd.gpt_raw;
129
+ const rangeStr = g.range_estimate && g.range_estimate !== "Unknown"
130
+ ? g.range_estimate + " (est.)" : "Unknown";
131
+ existing.features = {
132
+ "Type": g.object_type || "Unknown",
133
+ "Size": g.size || "Unknown",
134
+ "Threat Lvl": (g.threat_level || g.threat_level_score || "?") + "/10",
135
+ "Status": g.threat_classification || "?",
136
+ "Weapons": (g.visible_weapons || []).join(", ") || "None Visible",
137
+ "Readiness": g.weapon_readiness || "Unknown",
138
+ "Motion": g.motion_status || "Unknown",
139
+ "Range": rangeStr,
140
+ "Bearing": g.bearing || "Unknown",
141
+ "Intent": g.tactical_intent || "Unknown",
142
+ };
143
+ const dynFeats = g.dynamic_features || [];
144
+ for (const feat of dynFeats) {
145
+ if (feat && feat.key && feat.value) {
146
+ existing.features[feat.key] = feat.value;
147
+ }
148
+ }
149
+ existing.threat_level_score = rd.threat_level_score || g.threat_level_score || 0;
150
+ existing.threat_classification = rd.threat_classification || g.threat_classification || "Unknown";
151
+ existing.weapon_readiness = rd.weapon_readiness || g.weapon_readiness || "Unknown";
152
+ existing.gpt_distance_m = rd.gpt_distance_m || null;
153
+ existing.gpt_direction = rd.gpt_direction || null;
154
+ needsRender = true;
155
+ }
156
+ }
157
+ log(`Track cards updated with GPT assessment${logLabel ? " (" + logLabel + ")" : ""}`, "g");
158
+ }
159
+
160
+ if (needsRender && APP.ui && APP.ui.cards && APP.ui.cards.renderFrameTrackList) {
161
+ APP.ui.cards.renderFrameTrackList();
162
+ }
163
+
164
+ return needsRender;
165
+ };
166
+
167
  APP.api.client.pollAsyncJob = async function () {
168
  const { state } = APP.core;
169
  const { log, setHfStatus } = APP.ui.logging;
170
  const { fetchProcessedVideo, fetchDepthVideo, fetchDepthFirstFrame } = APP.core.video;
171
+ const syncGpt = APP.api.client._syncGptFromDetections;
172
 
173
  const pollInterval = 3000; // 3 seconds
174
  const maxAttempts = 200; // 10 minutes max
 
203
  log(`✓ Backend job ${completedJobId.substring(0, 8)}: completed successfully`, "g");
204
  setHfStatus("job completed, fetching video...");
205
 
206
+ // Final GPT sync — enrichment may have completed during
207
+ // processing but the poll never landed on a "processing"
208
+ // cycle that picked it up (common for segmentation mode
209
+ // where _enrich_first_frame_gpt is skipped).
210
+ if (status.first_frame_detections && status.first_frame_detections.length > 0) {
211
+ syncGpt(status.first_frame_detections, "final sync");
212
+ }
213
+
214
  try {
215
  await fetchProcessedVideo();
216
  await fetchDepthVideo();
 
244
 
245
  // Check if GPT enrichment has updated first-frame detections
246
  if (status.first_frame_detections && status.first_frame_detections.length > 0) {
247
+ syncGpt(status.first_frame_detections);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  }
249
  }
250