fix: judge camera catalog trimming by region, not by returned cameras

Coverage was checked only for packs among the cameras found, so an area
whose cameras were all trimmed away answered zero cameras, complete. The
catalog now reports where each trimmed pack's cameras are, and the tool
marks an answer partial whenever its area overlaps that region.
This commit is contained in:
Sameh Khamis committed 2026-10-02 15:21:36 -07:00
1 parent 7f56679a46
commit f610de77dc
5 files changed
+94 -6

No files matched your search

+20 -1
View File
@@ -31,6 +31,21 @@ async function readCatalog(services, signal) {
};
}
/** Whether an area's box overlaps a plain region box. */
function boxesOverlap(area, region) {
if (area.south > region.north || area.north < region.south) return false;
const spans =
area.west <= area.east
? [[area.west, area.east]]
: [
[area.west, 180],
[-180, area.east],
];
return spans.some(
([west, east]) => west <= region.east && east >= region.west,
);
}
async function readCameras(services, signal) {
return (await readCatalog(services, signal)).cameras;
}
@@ -55,8 +70,12 @@ export const findCctvCameras = defineTool({
const found = catalog.cameras.filter((camera) =>
areaContains(area, camera),
);
// A trimmed pack matters wherever its cameras are, served or not, so
// its region decides, not the cameras returned.
const packs = new Set(found.map((camera) => camera.pack).filter(Boolean));
const trimmed = catalog.trimmed.filter((entry) => packs.has(entry.pack));
const trimmed = catalog.trimmed.filter((entry) =>
entry.region ? boxesOverlap(area, entry.region) : packs.has(entry.pack),
);
const rows = found
.map((camera) => ({
id: camera.id,
+40
View File
@@ -282,3 +282,43 @@ test('cameras from a trimmed catalog pack are reported as partial', async () =>
/\(the catalog serves only some cameras here: 250 of 900 from tfl\)\.$/,
);
});
test('an area the catalog trimmed away entirely is not reported complete', async () => {
const catalog = composeCatalog({
tools: coreTools,
services: {
cctv: {
getCatalog: async () => ({
trimmedPacks: [
{
pack: 'tfl',
available: 900,
served: 250,
region: { west: -0.6, south: 51.3, east: 0.3, north: 51.7 },
},
],
// Every served camera is in central London, none in the suburb.
sources: [
{
id: 'jam-1',
name: 'Strand',
lat: 51.51,
lon: -0.12,
pack: 'tfl',
},
],
}),
},
},
});
const suburb = await catalog.call('find_cctv_cameras', {
area: { lat: 51.4, lon: -0.5, radius_km: 3 },
});
assert.equal(suburb.data.rows.length, 0);
assert.equal(suburb.data.complete, false);
assert.match(suburb.summary, /250 of 900 from tfl/);
const elsewhere = await catalog.call('find_cctv_cameras', {
area: { lat: 40.7, lon: -74, radius_km: 3 },
});
assert.equal(elsewhere.data.complete, true);
});