mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 10:14:51 +00:00
fix(slack): don't double-emit trailing text on odd number of ``` fences (#1436)
extractCodeBlocks split on "```" and treated every odd-indexed part as a closed code block. With an odd number of fences (>=3), Split returns an even number of parts, so the final segment -- which follows an unpaired opening fence and must stay literal -- landed on an odd index and was both emitted as a \x00CB\x00 placeholder (later restored as ```seg```) AND re-appended verbatim by the trailing len(parts)%2==0 block. The segment was therefore duplicated in the rendered Slack message (and blocks gained a spurious entry). Unclosed trailing fences are common in streamed LLM output. Exclude the final unpaired part from the paired-scan loop. Add a regression test covering the closed-block-then-unpaired-fence case.
This commit is contained in:
1 parent
27f4b7743b
commit
6f91b937c1
2 files changed
+18
-1
No files matched your search
@@ -113,8 +113,17 @@ func extractCodeBlocks(text string) (blocks []string, result string) {
|
||||
return nil, text
|
||||
}
|
||||
|
||||
// An odd number of ``` fences makes Split return an even number of parts;
|
||||
// the final segment then follows an unpaired opening fence and must stay
|
||||
// literal (it is re-appended verbatim below), so exclude it from the
|
||||
// paired-scan loop instead of misclassifying it as a closed code block.
|
||||
scanEnd := len(parts)
|
||||
if scanEnd%2 == 0 {
|
||||
scanEnd--
|
||||
}
|
||||
var sb strings.Builder
|
||||
for i, part := range parts {
|
||||
for i := 0; i < scanEnd; i++ {
|
||||
part := parts[i]
|
||||
if i%2 == 1 {
|
||||
blocks = append(blocks, part)
|
||||
sb.WriteString(fmt.Sprintf("\x00CB%d\x00", len(blocks)-1))
|
||||
|
||||
@@ -318,6 +318,14 @@ func TestExtractCodeBlocks(t *testing.T) {
|
||||
input: "text\n```\ncode (no closing)",
|
||||
expectedCount: 0,
|
||||
},
|
||||
{
|
||||
// A closed block followed by an unpaired opening fence: 3 fences ->
|
||||
// 4 parts. Only the middle part is a real code block; the trailing
|
||||
// segment must stay literal, not be counted (and re-emitted) as one.
|
||||
name: "closed block then unpaired trailing fence",
|
||||
input: "a```b```c```d",
|
||||
expectedCount: 1,
|
||||
},
|
||||
{
|
||||
name: "empty code block",
|
||||
input: "```\n\n```",
|
||||
|
||||
Reference in new issue
Block a user