Skip to content

Commit 7733316

Browse files
authored
Merge pull request #215 from htilly/develop
Merge develop into master: Performance optimizations and feature request improvements
2 parents d9c01b9 + 71fa4cb commit 7733316

7 files changed

Lines changed: 141 additions & 46 deletions

File tree

.github/agent/agent.mjs

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,48 +1229,48 @@ This is ONE FILE ONLY. Generate the complete diff now:`;
12291229
if (!filesToModify || allDiffs.length === 0) {
12301230
console.log(`[AGENT] Using single-pass generation method...`);
12311231
const apiStartTime = Date.now();
1232-
try {
1233-
output = await retryWithBackoff(async () => {
1234-
return await callAI(prompt);
1235-
}, 3, 1000);
1236-
const apiDuration = Date.now() - apiStartTime;
1237-
console.log(`[AGENT] API call completed in ${apiDuration}ms`);
1238-
} catch (error) {
1239-
const errorDetails = formatErrorDetails(error, {});
1240-
await handleError(error, `${provider.toUpperCase()} API Error`, { errorDetails });
1241-
// handleError calls process.exit(1), so we never reach here
1232+
try {
1233+
output = await retryWithBackoff(async () => {
1234+
return await callAI(prompt);
1235+
}, 3, 1000);
1236+
const apiDuration = Date.now() - apiStartTime;
1237+
console.log(`[AGENT] API call completed in ${apiDuration}ms`);
1238+
} catch (error) {
1239+
const errorDetails = formatErrorDetails(error, {});
1240+
await handleError(error, `${provider.toUpperCase()} API Error`, { errorDetails });
1241+
// handleError calls process.exit(1), so we never reach here
12421242
}
12431243
}
12441244

12451245
// Extract diff from potential markdown code blocks (only if using fallback method)
12461246
let diff = output.trim();
12471247
if (!filesToModify || allDiffs.length === 0) {
12481248
// Only do markdown extraction for fallback single-pass method
1249-
if (output.includes("```")) {
1250-
// Try to extract content between code fences
1251-
// Handle both single and multiple code blocks
1252-
const matches = output.matchAll(/```(?:diff)?\n([\s\S]*?)```/g);
1253-
const extractedDiffs = [];
1254-
for (const match of matches) {
1255-
extractedDiffs.push(match[1].trim());
1256-
}
1257-
// Use the longest extracted diff (likely the actual diff)
1258-
if (extractedDiffs.length > 0) {
1259-
diff = extractedDiffs.reduce((a, b) => a.length > b.length ? a : b);
1260-
}
1261-
1262-
// If no code blocks found but output contains diff markers, use the whole output
1263-
if (!diff.includes("--- a/") && output.includes("--- a/")) {
1264-
// Extract everything after the first "--- a/" line
1265-
const diffStart = output.indexOf("--- a/");
1266-
diff = output.substring(diffStart).trim();
1267-
// Remove any trailing markdown or explanations
1268-
const diffEnd = diff.indexOf("\n\n```") !== -1 ? diff.indexOf("\n\n```") :
1269-
diff.indexOf("\n\n##") !== -1 ? diff.indexOf("\n\n##") :
1270-
diff.indexOf("\n\n**") !== -1 ? diff.indexOf("\n\n**") :
1271-
diff.length;
1272-
diff = diff.substring(0, diffEnd).trim();
1273-
}
1249+
if (output.includes("```")) {
1250+
// Try to extract content between code fences
1251+
// Handle both single and multiple code blocks
1252+
const matches = output.matchAll(/```(?:diff)?\n([\s\S]*?)```/g);
1253+
const extractedDiffs = [];
1254+
for (const match of matches) {
1255+
extractedDiffs.push(match[1].trim());
1256+
}
1257+
// Use the longest extracted diff (likely the actual diff)
1258+
if (extractedDiffs.length > 0) {
1259+
diff = extractedDiffs.reduce((a, b) => a.length > b.length ? a : b);
1260+
}
1261+
1262+
// If no code blocks found but output contains diff markers, use the whole output
1263+
if (!diff.includes("--- a/") && output.includes("--- a/")) {
1264+
// Extract everything after the first "--- a/" line
1265+
const diffStart = output.indexOf("--- a/");
1266+
diff = output.substring(diffStart).trim();
1267+
// Remove any trailing markdown or explanations
1268+
const diffEnd = diff.indexOf("\n\n```") !== -1 ? diff.indexOf("\n\n```") :
1269+
diff.indexOf("\n\n##") !== -1 ? diff.indexOf("\n\n##") :
1270+
diff.indexOf("\n\n**") !== -1 ? diff.indexOf("\n\n**") :
1271+
diff.length;
1272+
diff = diff.substring(0, diffEnd).trim();
1273+
}
12741274
}
12751275
} else {
12761276
// For one-file-at-a-time, diff is already clean (output from combined diffs)
@@ -1372,9 +1372,9 @@ if (validationResult.errors.length > 0 || diff.match(/\+\+\+ b\/[^\n]*$/m)) {
13721372
}
13731373

13741374
const errorMsg = `${truncationReason}. The AI model may have generated an incomplete diff.\n\nErrors:\n${validationResult.errors.join('\n')}\n\nAPI Response Info:\n- Stop reason: ${stopReason}\n- Output tokens used: ${outputTokens}\n- Was truncated: ${wasTruncated}\n\nDiff preview (last 500 chars):\n\`\`\`\n${diff.substring(Math.max(0, diffLength - 500))}\n\`\`\`\n\nPossible causes:\n- Model hit output token limit (check max_tokens setting)\n- Input context too large, leaving insufficient room for output\n- Model stopped generating for other reasons\n\nSuggestions:\n- Reduce input context size (fewer files) - already reduced to 400KB\n- Break task into smaller parts\n- Verify max_tokens is sufficient (currently 180K)`;
1375-
const errorDetails = formatErrorDetails(new Error(errorMsg), { diff: diff.substring(Math.max(0, diffLength - 1000)), files: validationResult.stats.filesChanged });
1375+
const errorDetails = formatErrorDetails(new Error(errorMsg), { diff: diff.substring(Math.max(0, diffLength - 1000)), files: validationResult.stats.filesChanged });
13761376
await handleError(new Error(errorMsg), "Incomplete Diff (Truncated)", { diff: diff.substring(Math.max(0, diffLength - 1000)), errorDetails });
1377-
// handleError calls process.exit(1), so we never reach here
1377+
// handleError calls process.exit(1), so we never reach here
13781378
}
13791379
}
13801380
}

app.manifest.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,7 @@
5656

5757

5858

59+
60+
61+
62+

docs/DISCORD_DISCOVERY_CONTENT.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,7 @@ Join our Discord server for:
109109

110110

111111

112+
113+
114+
115+

index.js

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2340,15 +2340,15 @@ async function getNowPlaying(options = {}) {
23402340
// Only fetch queue if explicitly needed (skip for status polling to avoid choppy playback)
23412341
if (!skipQueue) {
23422342
promises.push(
2343-
sonos.getQueue().catch(err => {
2344-
// Ignore queue errors for now-playing
2345-
return null;
2346-
})
2343+
sonos.getQueue().catch(err => {
2344+
// Ignore queue errors for now-playing
2345+
return null;
2346+
})
23472347
);
23482348
} else {
23492349
promises.push(Promise.resolve(null));
23502350
}
2351-
2351+
23522352
const [state, volume, queue] = await Promise.all(promises);
23532353

23542354
// Fetch queue (next tracks) - only if we have queue data
@@ -4463,9 +4463,18 @@ async function _add(input, channel, userName) {
44634463
if (state === 'stopped') {
44644464
logger.info('Player stopped - ensuring queue is active source and flushing');
44654465
try {
4466-
// Parallel stop and flush (flush is safe even if not stopped)
4466+
// Stop any active playback to force Sonos to use queue
4467+
try {
4468+
await sonos.stop();
4469+
await new Promise(resolve => setTimeout(resolve, 300));
4470+
} catch (stopErr) {
4471+
// Ignore stop errors (might already be stopped)
4472+
logger.debug('Stop command result (may already be stopped): ' + stopErr.message);
4473+
}
4474+
4475+
// Flush queue to start fresh
44674476
await sonos.flush();
4468-
await new Promise(resolve => setTimeout(resolve, 200)); // Reduced from 300ms
4477+
await new Promise(resolve => setTimeout(resolve, 300));
44694478
logger.info('Queue flushed and ready');
44704479
} catch (flushErr) {
44714480
logger.warn('Could not flush queue: ' + flushErr.message);
@@ -4493,8 +4502,9 @@ async function _add(input, channel, userName) {
44934502
// Try to queue the first valid candidate (most relevant result)
44944503
let result = null;
44954504
try {
4505+
logger.info(`Attempting to queue: ${firstCandidate.name} by ${firstCandidate.artist} (URI: ${firstCandidate.uri})`);
44964506
await sonos.queue(firstCandidate.uri);
4497-
logger.info('Added track: ' + firstCandidate.name);
4507+
logger.info('Successfully queued track: ' + firstCandidate.name);
44984508
result = firstCandidate;
44994509
} catch (e) {
45004510
const errorDetails = e.message || String(e);
@@ -4540,7 +4550,72 @@ async function _add(input, channel, userName) {
45404550
// Start playback in background (don't await)
45414551
(async () => {
45424552
try {
4543-
await new Promise(resolve => setTimeout(resolve, 300)); // Brief delay for queue to settle
4553+
// Ensure queue is the active source before starting playback
4554+
// Stop any active playback to force Sonos to use queue
4555+
try {
4556+
await sonos.stop();
4557+
await new Promise(resolve => setTimeout(resolve, 500));
4558+
} catch (stopErr) {
4559+
// Ignore stop errors (might already be stopped)
4560+
logger.debug('Stop before play (may already be stopped): ' + stopErr.message);
4561+
}
4562+
4563+
// Verify queue has items before trying to play (prevents UPnP error 701)
4564+
let queueReady = false;
4565+
let retries = 0;
4566+
while (!queueReady && retries < 5) {
4567+
try {
4568+
const queue = await sonos.getQueue();
4569+
if (queue && queue.items && queue.items.length > 0) {
4570+
queueReady = true;
4571+
logger.debug(`Queue verified: ${queue.items.length} items ready`);
4572+
} else {
4573+
logger.debug(`Queue not ready yet (attempt ${retries + 1}/5), waiting...`);
4574+
await new Promise(resolve => setTimeout(resolve, 300));
4575+
retries++;
4576+
}
4577+
} catch (queueErr) {
4578+
logger.debug(`Queue check failed (attempt ${retries + 1}/5): ${queueErr.message}`);
4579+
await new Promise(resolve => setTimeout(resolve, 300));
4580+
retries++;
4581+
}
4582+
}
4583+
4584+
if (!queueReady) {
4585+
logger.warn('Queue not ready after 5 attempts, attempting playback anyway');
4586+
}
4587+
4588+
// Try to activate queue by seeking to position 1 (alternative to SetAVTransportURI)
4589+
// This should activate the queue as the transport source
4590+
try {
4591+
logger.debug('Attempting to seek to queue position 1 to activate queue');
4592+
// Seek to track 1 in the queue to activate it
4593+
await sonos.avTransportService().Seek({
4594+
InstanceID: 0,
4595+
Unit: 'TRACK_NR',
4596+
Target: '1'
4597+
});
4598+
logger.debug('Successfully sought to track 1, queue should be active');
4599+
// Wait for seek to complete
4600+
await new Promise(resolve => setTimeout(resolve, 500));
4601+
} catch (seekErr) {
4602+
// If seek fails, try alternative: use next() to jump to first track
4603+
logger.debug('Seek failed, trying next() to activate queue: ' + seekErr.message);
4604+
try {
4605+
// Jump to first track in queue (this should activate the queue)
4606+
await sonos.next();
4607+
logger.debug('Used next() to activate queue');
4608+
await new Promise(resolve => setTimeout(resolve, 300));
4609+
} catch (nextErr) {
4610+
logger.debug('next() also failed: ' + nextErr.message);
4611+
// Continue anyway - play() might still work
4612+
}
4613+
}
4614+
4615+
// Wait a moment to ensure queue is ready
4616+
await new Promise(resolve => setTimeout(resolve, 500));
4617+
4618+
// Start playback from queue
45444619
await sonos.play();
45454620
logger.info('Started playback from queue');
45464621
} catch (playErr) {

lib/slack-validator.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,7 @@ module.exports = {
9999

100100

101101

102+
103+
104+
105+

lib/sonos-discovery.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,7 @@ module.exports = {
147147

148148

149149

150+
151+
152+
153+

lib/spotify-validator.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,7 @@ module.exports = {
6363

6464

6565

66+
67+
68+
69+

0 commit comments

Comments
 (0)