Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Whitestone
571475a749 fix: closes #716
Some checks failed
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 18s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-12 12:44:37 -04:00
2 changed files with 12 additions and 57 deletions

View File

@@ -694,61 +694,15 @@ const SpatialMemory = (() => {
}
}
// ─── CONTEXT COMPACTION (issue #675) ──────────────────
const COMPACT_CONTENT_MAXLEN = 80; // max chars for low-strength memories
const COMPACT_STRENGTH_THRESHOLD = 0.5; // below this, content gets truncated
const COMPACT_MAX_CONNECTIONS = 5; // cap connections per memory
const COMPACT_POSITION_DECIMALS = 1; // round positions to 1 decimal
function _compactPosition(pos) {
const factor = Math.pow(10, COMPACT_POSITION_DECIMALS);
return pos.map(v => Math.round(v * factor) / factor);
}
/**
* Deterministically compact a memory for storage.
* Same input always produces same output — no randomness.
* Strong memories keep full fidelity; weak memories get truncated.
*/
function _compactMemory(o) {
const strength = o.mesh.userData.strength || 0.7;
const content = o.data.content || '';
const connections = o.data.connections || [];
// Deterministic content truncation for weak memories
let compactContent = content;
if (strength < COMPACT_STRENGTH_THRESHOLD && content.length > COMPACT_CONTENT_MAXLEN) {
compactContent = content.slice(0, COMPACT_CONTENT_MAXLEN) + '\u2026';
}
// Cap connections (keep first N, deterministic)
const compactConnections = connections.length > COMPACT_MAX_CONNECTIONS
? connections.slice(0, COMPACT_MAX_CONNECTIONS)
: connections;
return {
id: o.data.id,
content: compactContent,
category: o.region,
position: _compactPosition([o.mesh.position.x, o.mesh.position.y - 1.5, o.mesh.position.z]),
source: o.data.source || 'unknown',
timestamp: o.data.timestamp || o.mesh.userData.createdAt,
strength: Math.round(strength * 100) / 100, // 2 decimal precision
connections: compactConnections
};
}
// ─── PERSISTENCE ─────────────────────────────────────
function exportIndex(options = {}) {
const compact = options.compact !== false; // compact by default
function exportIndex() {
return {
version: 1,
exportedAt: new Date().toISOString(),
compacted: compact,
regions: Object.fromEntries(
Object.entries(REGIONS).map(([k, v]) => [k, { label: v.label, center: v.center, radius: v.radius, color: v.color }])
),
memories: Object.values(_memoryObjects).map(o => compact ? _compactMemory(o) : {
memories: Object.values(_memoryObjects).map(o => ({
id: o.data.id,
content: o.data.content,
category: o.region,
@@ -757,7 +711,7 @@ const SpatialMemory = (() => {
timestamp: o.data.timestamp || o.mesh.userData.createdAt,
strength: o.mesh.userData.strength || 0.7,
connections: o.data.connections || []
})
}))
};
}

View File

@@ -52,19 +52,20 @@ async def broadcast_handler(websocket: websockets.WebSocketServerProtocol):
continue
disconnected = set()
# Create broadcast tasks for efficiency
tasks = []
# Create broadcast tasks, tracking which client each task targets
task_client_pairs = []
for client in clients:
if client != websocket and client.open:
tasks.append(asyncio.create_task(client.send(message)))
if tasks:
task = asyncio.create_task(client.send(message))
task_client_pairs.append((task, client))
if task_client_pairs:
tasks = [pair[0] for pair in task_client_pairs]
results = await asyncio.gather(*tasks, return_exceptions=True)
for i, result in enumerate(results):
if isinstance(result, Exception):
# Find the client that failed
target_client = [c for c in clients if c != websocket][i]
logger.error(f"Failed to send to a client {target_client.remote_address}: {result}")
target_client = task_client_pairs[i][1]
logger.error(f"Failed to send to client {target_client.remote_address}: {result}")
disconnected.add(target_client)
if disconnected: