fix(ci): compact commit list + bigger token budget for release announce [skip ci]
The v3.0.2 announce (PR #263, 45 commits) never reached Discord: the changelog prompt carried every commit body (52 KB), deepseek-v4-flash spent its whole 6000-token budget reasoning, returned empty content and the guard aborted the run before posting. Past 8000 chars of commit detail we now send hash + subject only (52916 -> 3699 chars on that range), raise the budgets to 32000/16000, and retry once with double when content still comes back empty. Also log finish_reason and completion_tokens so the next empty answer is diagnosable from the run log alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
588c356e7e
commit
388b013ac4
|
|
@ -59,8 +59,10 @@ jobs:
|
||||||
// deepseek-v4-flash is a REASONING model: max_tokens caps the
|
// deepseek-v4-flash is a REASONING model: max_tokens caps the
|
||||||
// reasoning trace + the visible answer TOGETHER, so a stingy budget
|
// reasoning trace + the visible answer TOGETHER, so a stingy budget
|
||||||
// makes `content` come back empty (which once made us POST an empty
|
// makes `content` come back empty (which once made us POST an empty
|
||||||
// Discord message). Give it generous budgets.
|
// Discord message, and once killed the v3.0.2 announce outright).
|
||||||
async function callDeepSeek(prompt, maxTokens, temperature = 0.3) {
|
// Give it generous budgets, and retry with double when the model
|
||||||
|
// spends the whole budget reasoning.
|
||||||
|
async function deepSeekRequest(prompt, maxTokens, temperature) {
|
||||||
const apiKey = process.env.DAVEAGENT_API_KEY;
|
const apiKey = process.env.DAVEAGENT_API_KEY;
|
||||||
if (!apiKey) throw new Error('DAVEAGENT_API_KEY not set');
|
if (!apiKey) throw new Error('DAVEAGENT_API_KEY not set');
|
||||||
const res = await fetch('https://api.deepseek.com/v1/chat/completions', {
|
const res = await fetch('https://api.deepseek.com/v1/chat/completions', {
|
||||||
|
|
@ -81,7 +83,21 @@ jobs:
|
||||||
throw new Error(`DeepSeek ${res.status}: ${err}`);
|
throw new Error(`DeepSeek ${res.status}: ${err}`);
|
||||||
}
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
return (data.choices?.[0]?.message?.content ?? '').trim();
|
const choice = data.choices?.[0] ?? {};
|
||||||
|
console.log(
|
||||||
|
`DeepSeek: max_tokens=${maxTokens} finish_reason=${choice.finish_reason} ` +
|
||||||
|
`completion_tokens=${data.usage?.completion_tokens ?? '?'}`
|
||||||
|
);
|
||||||
|
return (choice.message?.content ?? '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callDeepSeek(prompt, maxTokens, temperature = 0.3) {
|
||||||
|
let content = await deepSeekRequest(prompt, maxTokens, temperature);
|
||||||
|
if (!content) {
|
||||||
|
console.log('Empty content — retrying once with double the token budget');
|
||||||
|
content = await deepSeekRequest(prompt, maxTokens * 2, temperature);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Version (read from release's package.json) ────────────────────
|
// ── Version (read from release's package.json) ────────────────────
|
||||||
|
|
@ -120,15 +136,27 @@ jobs:
|
||||||
console.log(`Event: ${context.eventName} PR: #${prNumber} "${prTitle}"`);
|
console.log(`Event: ${context.eventName} PR: #${prNumber} "${prTitle}"`);
|
||||||
console.log(`Range: ${baseSha.slice(0,7)}..${headSha.slice(0,7)} (${sourceBranch} → release)`);
|
console.log(`Range: ${baseSha.slice(0,7)}..${headSha.slice(0,7)} (${sourceBranch} → release)`);
|
||||||
|
|
||||||
const commits = git(
|
// Full detail (subject + BODY + author) reads best for a small
|
||||||
|
// release, but a 45-commit range blows the prompt up and the model
|
||||||
|
// burns its whole budget reasoning over it. Past the threshold,
|
||||||
|
// drop the bodies and send subjects only.
|
||||||
|
const MAX_COMMIT_CHARS = 8000;
|
||||||
|
const commitsFull = git(
|
||||||
`log ${baseSha}..${headSha} --pretty=format:"%h||%s||%b||%an|||" --no-merges`
|
`log ${baseSha}..${headSha} --pretty=format:"%h||%s||%b||%an|||" --no-merges`
|
||||||
) || git(`log -50 --pretty=format:"%h||%s||%b||%an|||" --no-merges`);
|
) || git(`log -50 --pretty=format:"%h||%s||%b||%an|||" --no-merges`);
|
||||||
const commitLines = git(
|
const commitLines = git(
|
||||||
`log ${baseSha}..${headSha} --pretty=format:"%h %s" --no-merges`
|
`log ${baseSha}..${headSha} --pretty=format:"%h %s" --no-merges`
|
||||||
) || git(`log -50 --pretty=format:"%h %s" --no-merges`);
|
) || git(`log -50 --pretty=format:"%h %s" --no-merges`);
|
||||||
|
const commitCount = commitLines.split('\n').filter(Boolean).length;
|
||||||
|
const compact = commitsFull.length > MAX_COMMIT_CHARS;
|
||||||
|
// Subjects alone can still be huge on a monster range — hard cap.
|
||||||
|
const commits = (compact ? commitLines : commitsFull).slice(0, 4 * MAX_COMMIT_CHARS);
|
||||||
const changedFiles = git(`diff --name-only ${baseSha}..${headSha}`)
|
const changedFiles = git(`diff --name-only ${baseSha}..${headSha}`)
|
||||||
.split('\n').filter(Boolean);
|
.split('\n').filter(Boolean);
|
||||||
console.log(`Commits found: ${commitLines.split('\n').filter(Boolean).length}`);
|
console.log(
|
||||||
|
`Commits found: ${commitCount} — sending ${compact ? 'SUBJECTS ONLY' : 'full detail'} ` +
|
||||||
|
`(${commits.length} chars, full detail would be ${commitsFull.length})`
|
||||||
|
);
|
||||||
console.log(`Files changed: ${changedFiles.length}`);
|
console.log(`Files changed: ${changedFiles.length}`);
|
||||||
|
|
||||||
// ──────────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -142,7 +170,7 @@ jobs:
|
||||||
PR DESCRIPTION: ${prBody}
|
PR DESCRIPTION: ${prBody}
|
||||||
SOURCE BRANCH: ${sourceBranch}
|
SOURCE BRANCH: ${sourceBranch}
|
||||||
|
|
||||||
GIT COMMITS (hash||subject||body||author):
|
GIT COMMITS (${compact ? 'hash subject' : 'hash||subject||body||author'}):
|
||||||
${commits}
|
${commits}
|
||||||
|
|
||||||
Generate a changelog entry using EXACTLY this format:
|
Generate a changelog entry using EXACTLY this format:
|
||||||
|
|
@ -167,8 +195,11 @@ jobs:
|
||||||
6. Only include sections that have real content
|
6. Only include sections that have real content
|
||||||
7. Output ONLY the changelog entry block, nothing else`;
|
7. Output ONLY the changelog entry block, nothing else`;
|
||||||
|
|
||||||
console.log('Generating CHANGELOG entry...');
|
console.log(`Generating CHANGELOG entry... (prompt: ${changelogPrompt.length} chars)`);
|
||||||
const changelogEntry = await callDeepSeek(changelogPrompt, 6000, 0.3);
|
// 16000 was measured to still hit finish_reason=length (all of it
|
||||||
|
// spent reasoning, empty content) on a 45-commit range. 32000 lands
|
||||||
|
// comfortably; the retry doubles to 64000, which the API accepts.
|
||||||
|
const changelogEntry = await callDeepSeek(changelogPrompt, 32000, 0.3);
|
||||||
if (!changelogEntry) {
|
if (!changelogEntry) {
|
||||||
core.setFailed('CHANGELOG generation returned empty content — aborting before any side effects');
|
core.setFailed('CHANGELOG generation returned empty content — aborting before any side effects');
|
||||||
return;
|
return;
|
||||||
|
|
@ -189,7 +220,7 @@ jobs:
|
||||||
${changelogEntry}
|
${changelogEntry}
|
||||||
|
|
||||||
RECENT COMMITS (for additional context):
|
RECENT COMMITS (for additional context):
|
||||||
${commitLines}
|
${commitLines.slice(0, MAX_COMMIT_CHARS)}
|
||||||
|
|
||||||
STYLE EXAMPLES:
|
STYLE EXAMPLES:
|
||||||
|
|
||||||
|
|
@ -232,7 +263,7 @@ jobs:
|
||||||
- Write in English`;
|
- Write in English`;
|
||||||
|
|
||||||
console.log('Generating Discord announcement...');
|
console.log('Generating Discord announcement...');
|
||||||
let announcement = await callDeepSeek(announcementPrompt, 4000, 0.7);
|
let announcement = await callDeepSeek(announcementPrompt, 16000, 0.7);
|
||||||
|
|
||||||
// Limpiar markdown residual
|
// Limpiar markdown residual
|
||||||
announcement = announcement
|
announcement = announcement
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue