fix(discord): rebalance reasoning italics
This commit is contained in:
@@ -36,6 +36,7 @@
|
|||||||
- Models: normalize `${ENV_VAR}` apiKey config values and auto-fill missing provider `apiKey` from env/auth when custom provider models are configured (fixes MiniMax “Unknown model” on fresh installs).
|
- Models: normalize `${ENV_VAR}` apiKey config values and auto-fill missing provider `apiKey` from env/auth when custom provider models are configured (fixes MiniMax “Unknown model” on fresh installs).
|
||||||
- Models/Tools: include `MiniMax-VL-01` in implicit MiniMax provider so image pairing uses a real vision model.
|
- Models/Tools: include `MiniMax-VL-01` in implicit MiniMax provider so image pairing uses a real vision model.
|
||||||
- Telegram: show typing indicator in General forum topics. (#779) — thanks @azade-c.
|
- Telegram: show typing indicator in General forum topics. (#779) — thanks @azade-c.
|
||||||
|
- Discord: keep reasoning italics intact when messages are chunked, so reasoning stays italic across multi-part sends.
|
||||||
- Models: keep explicit GitHub Copilot provider config and honor agent-dir auth profiles for auto-injection. (#705) — thanks @TAGOOZ.
|
- Models: keep explicit GitHub Copilot provider config and honor agent-dir auth profiles for auto-injection. (#705) — thanks @TAGOOZ.
|
||||||
- Auto-reply: restore 300-char heartbeat ack limit and keep >300 char replies instead of dropping them; adjust long heartbeat test content accordingly.
|
- Auto-reply: restore 300-char heartbeat ack limit and keep >300 char replies instead of dropping them; adjust long heartbeat test content accordingly.
|
||||||
- Gateway: `agents.list` now honors explicit `agents.list` config without pulling stray agents from disk; GitHub Copilot CLI auth path uses the updated provider build.
|
- Gateway: `agents.list` now honors explicit `agents.list` config without pulling stray agents from disk; GitHub Copilot CLI auth path uses the updated provider build.
|
||||||
|
|||||||
@@ -67,4 +67,64 @@ describe("chunkDiscordText", () => {
|
|||||||
expect(hasBalancedFences(chunk)).toBe(true);
|
expect(hasBalancedFences(chunk)).toBe(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps reasoning italics balanced across chunks", () => {
|
||||||
|
const body = Array.from({ length: 25 }, (_, i) => `${i + 1}. line`).join(
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
const text = `Reasoning:\n_${body}_`;
|
||||||
|
|
||||||
|
const chunks = chunkDiscordText(text, { maxLines: 10, maxChars: 2000 });
|
||||||
|
expect(chunks.length).toBeGreaterThan(1);
|
||||||
|
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
// Each chunk should have balanced italics markers (even count).
|
||||||
|
const count = (chunk.match(/_/g) || []).length;
|
||||||
|
expect(count % 2).toBe(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure italics reopen on subsequent chunks
|
||||||
|
expect(chunks[0]).toContain("_1. line");
|
||||||
|
// Second chunk should reopen italics at the start
|
||||||
|
expect(chunks[1].trimStart().startsWith("_")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps reasoning italics balanced when chunks split by char limit", () => {
|
||||||
|
const longLine = "This is a very long reasoning line that forces char splits.";
|
||||||
|
const body = Array.from({ length: 5 }, () => longLine).join("\n");
|
||||||
|
const text = `Reasoning:\n_${body}_`;
|
||||||
|
|
||||||
|
const chunks = chunkDiscordText(text, { maxChars: 80, maxLines: 50 });
|
||||||
|
expect(chunks.length).toBeGreaterThan(1);
|
||||||
|
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
const underscoreCount = (chunk.match(/_/g) || []).length;
|
||||||
|
expect(underscoreCount % 2).toBe(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reopens italics while preserving leading whitespace on following chunk", () => {
|
||||||
|
const body = [
|
||||||
|
"1. line",
|
||||||
|
"2. line",
|
||||||
|
"3. line",
|
||||||
|
"4. line",
|
||||||
|
"5. line",
|
||||||
|
"6. line",
|
||||||
|
"7. line",
|
||||||
|
"8. line",
|
||||||
|
"9. line",
|
||||||
|
"10. line",
|
||||||
|
" 11. indented line",
|
||||||
|
"12. line",
|
||||||
|
].join("\n");
|
||||||
|
const text = `Reasoning:\n_${body}_`;
|
||||||
|
|
||||||
|
const chunks = chunkDiscordText(text, { maxLines: 10, maxChars: 2000 });
|
||||||
|
expect(chunks.length).toBeGreaterThan(1);
|
||||||
|
|
||||||
|
const second = chunks[1];
|
||||||
|
expect(second.startsWith("_")).toBe(true);
|
||||||
|
expect(second).toContain(" 11. indented line");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -187,5 +187,42 @@ export function chunkDiscordText(
|
|||||||
if (payload.trim().length) chunks.push(payload);
|
if (payload.trim().length) chunks.push(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunks;
|
return rebalanceReasoningItalics(text, chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep italics intact for reasoning payloads that are wrapped once with `_…_`.
|
||||||
|
// When Discord chunking splits the message, we close italics at the end of
|
||||||
|
// each chunk and reopen at the start of the next so every chunk renders
|
||||||
|
// consistently.
|
||||||
|
function rebalanceReasoningItalics(source: string, chunks: string[]): string[] {
|
||||||
|
if (chunks.length <= 1) return chunks;
|
||||||
|
|
||||||
|
const opensWithReasoningItalics =
|
||||||
|
source.startsWith("Reasoning:\n_") && source.trimEnd().endsWith("_");
|
||||||
|
if (!opensWithReasoningItalics) return chunks;
|
||||||
|
|
||||||
|
const adjusted = [...chunks];
|
||||||
|
for (let i = 0; i < adjusted.length; i++) {
|
||||||
|
const isLast = i === adjusted.length - 1;
|
||||||
|
const current = adjusted[i];
|
||||||
|
|
||||||
|
// Ensure current chunk closes italics so Discord renders it italicized.
|
||||||
|
const needsClosing = !current.trimEnd().endsWith("_");
|
||||||
|
if (needsClosing) {
|
||||||
|
adjusted[i] = `${current}_`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLast) break;
|
||||||
|
|
||||||
|
// Re-open italics on the next chunk if needed.
|
||||||
|
const next = adjusted[i + 1];
|
||||||
|
const leadingWhitespaceLen = next.length - next.trimStart().length;
|
||||||
|
const leadingWhitespace = next.slice(0, leadingWhitespaceLen);
|
||||||
|
const nextBody = next.slice(leadingWhitespaceLen);
|
||||||
|
if (!nextBody.startsWith("_")) {
|
||||||
|
adjusted[i + 1] = `${leadingWhitespace}_${nextBody}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjusted;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user