mirror of
https://github.com/coalaura/whiskr.git
synced 2025-12-02 20:22:52 +00:00
better markdown parsing
This commit is contained in:
BIN
.github/images.png
vendored
Normal file
BIN
.github/images.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 653 KiB |
76
chat.go
76
chat.go
@@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/revrost/go-openrouter"
|
"github.com/revrost/go-openrouter"
|
||||||
@@ -436,78 +435,3 @@ func RunCompletion(ctx context.Context, response *Stream, request *openrouter.Ch
|
|||||||
|
|
||||||
return tool, buf.String(), nil
|
return tool, buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SplitImagePairs(text string) []openrouter.ChatMessagePart {
|
|
||||||
rgx := regexp.MustCompile(`(?m)!\[[^\]]*]\((\S+?)\)`)
|
|
||||||
|
|
||||||
var (
|
|
||||||
index int
|
|
||||||
parts []openrouter.ChatMessagePart
|
|
||||||
)
|
|
||||||
|
|
||||||
push := func(str, end int) {
|
|
||||||
if str > end {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rest := text[str:end]
|
|
||||||
|
|
||||||
if rest == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
total := len(parts)
|
|
||||||
|
|
||||||
if total > 0 && parts[total-1].Type == openrouter.ChatMessagePartTypeText {
|
|
||||||
parts[total-1].Text += rest
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parts = append(parts, openrouter.ChatMessagePart{
|
|
||||||
Type: openrouter.ChatMessagePartTypeText,
|
|
||||||
Text: rest,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
location := rgx.FindStringSubmatchIndex(text[index:])
|
|
||||||
if location == nil {
|
|
||||||
push(index, len(text)-1)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
start := index + location[0]
|
|
||||||
end := index + location[1]
|
|
||||||
|
|
||||||
urlStart := index + location[2]
|
|
||||||
urlEnd := index + location[3]
|
|
||||||
|
|
||||||
url := text[urlStart:urlEnd]
|
|
||||||
|
|
||||||
if !strings.HasPrefix(url, "https://") && !strings.HasPrefix(url, "http://") {
|
|
||||||
push(index, end)
|
|
||||||
|
|
||||||
index = end
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if start > index {
|
|
||||||
push(index, start)
|
|
||||||
}
|
|
||||||
|
|
||||||
parts = append(parts, openrouter.ChatMessagePart{
|
|
||||||
Type: openrouter.ChatMessagePartTypeImageURL,
|
|
||||||
ImageURL: &openrouter.ChatMessageImageURL{
|
|
||||||
Detail: openrouter.ImageURLDetailAuto,
|
|
||||||
URL: url,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
index = end
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts
|
|
||||||
}
|
|
||||||
|
|||||||
132
markdown.go
Normal file
132
markdown.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/revrost/go-openrouter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeRegion struct {
|
||||||
|
Start int
|
||||||
|
End int
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindMarkdownCodeRegions(text string) []CodeRegion {
|
||||||
|
var regions []CodeRegion
|
||||||
|
|
||||||
|
inline := regexp.MustCompile(`\x60[^\x60\n]+?\x60`)
|
||||||
|
|
||||||
|
for _, match := range inline.FindAllStringIndex(text, -1) {
|
||||||
|
regions = append(regions, CodeRegion{
|
||||||
|
Start: match[0],
|
||||||
|
End: match[1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fenced := regexp.MustCompile(`(?m)^\x60\x60\x60[^\n]*\n(.*?\n)^\x60\x60\x60\s*$`)
|
||||||
|
|
||||||
|
for _, match := range fenced.FindAllStringIndex(text, -1) {
|
||||||
|
regions = append(regions, CodeRegion{
|
||||||
|
Start: match[0],
|
||||||
|
End: match[1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return regions
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInsideCodeBlock(pos int, regions []CodeRegion) bool {
|
||||||
|
for _, region := range regions {
|
||||||
|
if pos >= region.Start && pos < region.End {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func SplitImagePairs(text string) []openrouter.ChatMessagePart {
|
||||||
|
code := FindMarkdownCodeRegions(text)
|
||||||
|
|
||||||
|
rgx := regexp.MustCompile(`(?m)!\[[^\]]*]\((\S+?)\)`)
|
||||||
|
|
||||||
|
var (
|
||||||
|
index int
|
||||||
|
parts []openrouter.ChatMessagePart
|
||||||
|
)
|
||||||
|
|
||||||
|
push := func(str, end int) {
|
||||||
|
if str > end {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rest := text[str:end]
|
||||||
|
|
||||||
|
if rest == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total := len(parts)
|
||||||
|
|
||||||
|
if total > 0 && parts[total-1].Type == openrouter.ChatMessagePartTypeText {
|
||||||
|
parts[total-1].Text += rest
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = append(parts, openrouter.ChatMessagePart{
|
||||||
|
Type: openrouter.ChatMessagePartTypeText,
|
||||||
|
Text: rest,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
location := rgx.FindStringSubmatchIndex(text[index:])
|
||||||
|
if location == nil {
|
||||||
|
push(index, len(text)-1)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
start := index + location[0]
|
||||||
|
end := index + location[1]
|
||||||
|
|
||||||
|
if IsInsideCodeBlock(start, code) {
|
||||||
|
push(index, end)
|
||||||
|
|
||||||
|
index = end
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
urlStart := index + location[2]
|
||||||
|
urlEnd := index + location[3]
|
||||||
|
|
||||||
|
url := text[urlStart:urlEnd]
|
||||||
|
|
||||||
|
if !strings.HasPrefix(url, "https://") && !strings.HasPrefix(url, "http://") {
|
||||||
|
push(index, end)
|
||||||
|
|
||||||
|
index = end
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if start > index {
|
||||||
|
push(index, start)
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = append(parts, openrouter.ChatMessagePart{
|
||||||
|
Type: openrouter.ChatMessagePartTypeImageURL,
|
||||||
|
ImageURL: &openrouter.ChatMessageImageURL{
|
||||||
|
Detail: openrouter.ImageURLDetailAuto,
|
||||||
|
URL: url,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
index = end
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts
|
||||||
|
}
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
this.#build(collapsed);
|
this.#build(collapsed);
|
||||||
this.#render();
|
this.#render();
|
||||||
|
|
||||||
if (tool) {
|
if (tool?.name) {
|
||||||
this.setTool(tool);
|
this.setTool(tool);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,6 +592,8 @@
|
|||||||
|
|
||||||
this.#_message.classList.toggle("has-tool", !!this.#tool);
|
this.#_message.classList.toggle("has-tool", !!this.#tool);
|
||||||
|
|
||||||
|
this.#updateToolHeight();
|
||||||
|
|
||||||
noScroll || scroll();
|
noScroll || scroll();
|
||||||
|
|
||||||
updateScrollButton();
|
updateScrollButton();
|
||||||
@@ -631,6 +633,8 @@
|
|||||||
|
|
||||||
if (!only || only === "reasoning") {
|
if (!only || only === "reasoning") {
|
||||||
this.#patch("reasoning", this.#_reasoning, this.#reasoning, () => {
|
this.#patch("reasoning", this.#_reasoning, this.#reasoning, () => {
|
||||||
|
this.#updateReasoningHeight();
|
||||||
|
|
||||||
noScroll || scroll();
|
noScroll || scroll();
|
||||||
|
|
||||||
updateScrollButton();
|
updateScrollButton();
|
||||||
|
|||||||
Reference in New Issue
Block a user