perf: pre-compile regexes at package level
Move regexp.MustCompile calls from inside methods to package-level variables in web.go (7 regexes) and loader.go (2 regexes). This avoids repeated compilation on every invocation. Amp-Thread-ID: https://ampcode.com/threads/T-019c79c3-ea1c-7471-b09d-be90ba0e1ca0 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
parent
e599573ed4
commit
57c9bd03d3
2 changed files with 28 additions and 28 deletions
|
|
@ -13,7 +13,11 @@ import (
|
||||||
"github.com/sipeed/picoclaw/pkg/logger"
|
"github.com/sipeed/picoclaw/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var namePattern = regexp.MustCompile(`^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$`)
|
var (
|
||||||
|
namePattern = regexp.MustCompile(`^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$`)
|
||||||
|
reFrontmatter = regexp.MustCompile(`(?s)^---(?:\r\n|\n|\r)(.*?)(?:\r\n|\n|\r)---`)
|
||||||
|
reStripFrontmatter = regexp.MustCompile(`(?s)^---(?:\r\n|\n|\r)(.*?)(?:\r\n|\n|\r)---(?:\r\n|\n|\r)*`)
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MaxNameLength = 64
|
MaxNameLength = 64
|
||||||
|
|
@ -318,11 +322,7 @@ func (sl *SkillsLoader) parseSimpleYAML(content string) map[string]string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sl *SkillsLoader) extractFrontmatter(content string) string {
|
func (sl *SkillsLoader) extractFrontmatter(content string) string {
|
||||||
// Support \n (Unix), \r\n (Windows), and \r (classic Mac) line endings for frontmatter blocks
|
match := reFrontmatter.FindStringSubmatch(content)
|
||||||
// (?s) enables DOTALL so . matches newlines;
|
|
||||||
// ^--- at start, then ... --- at start of line, honoring all three line ending types
|
|
||||||
re := regexp.MustCompile(`(?s)^---(?:\r\n|\n|\r)(.*?)(?:\r\n|\n|\r)---`)
|
|
||||||
match := re.FindStringSubmatch(content)
|
|
||||||
if len(match) > 1 {
|
if len(match) > 1 {
|
||||||
return match[1]
|
return match[1]
|
||||||
}
|
}
|
||||||
|
|
@ -330,12 +330,7 @@ func (sl *SkillsLoader) extractFrontmatter(content string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sl *SkillsLoader) stripFrontmatter(content string) string {
|
func (sl *SkillsLoader) stripFrontmatter(content string) string {
|
||||||
// Support \n (Unix), \r\n (Windows), and \r (classic Mac) line endings for frontmatter blocks
|
return reStripFrontmatter.ReplaceAllString(content, "")
|
||||||
// (?s) enables DOTALL so . matches newlines;
|
|
||||||
// ^--- at start, then ... --- at start of line, honoring all three line ending types
|
|
||||||
// Match zero or more trailing line endings after closing --- (handles both with and without blank lines)
|
|
||||||
re := regexp.MustCompile(`(?s)^---(?:\r\n|\n|\r)(.*?)(?:\r\n|\n|\r)---(?:\r\n|\n|\r)*`)
|
|
||||||
return re.ReplaceAllString(content, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func escapeXML(s string) string {
|
func escapeXML(s string) string {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,19 @@ const (
|
||||||
userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Pre-compiled regexes for HTML text extraction
|
||||||
|
var (
|
||||||
|
reScript = regexp.MustCompile(`<script[\s\S]*?</script>`)
|
||||||
|
reStyle = regexp.MustCompile(`<style[\s\S]*?</style>`)
|
||||||
|
reTags = regexp.MustCompile(`<[^>]+>`)
|
||||||
|
reWhitespace = regexp.MustCompile(`[^\S\n]+`)
|
||||||
|
reBlankLines = regexp.MustCompile(`\n{3,}`)
|
||||||
|
|
||||||
|
// DuckDuckGo result extraction
|
||||||
|
reDDGLink = regexp.MustCompile(`<a[^>]*class="[^"]*result__a[^"]*"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)</a>`)
|
||||||
|
reDDGSnippet = regexp.MustCompile(`<a class="result__snippet[^"]*".*?>([\s\S]*?)</a>`)
|
||||||
|
)
|
||||||
|
|
||||||
type SearchProvider interface {
|
type SearchProvider interface {
|
||||||
Search(ctx context.Context, query string, count int) (string, error)
|
Search(ctx context.Context, query string, count int) (string, error)
|
||||||
}
|
}
|
||||||
|
|
@ -118,8 +131,7 @@ func (p *DuckDuckGoSearchProvider) extractResults(html string, count int, query
|
||||||
// Try finding the result links directly first, as they are the most critical
|
// Try finding the result links directly first, as they are the most critical
|
||||||
// Pattern: <a class="result__a" href="...">Title</a>
|
// Pattern: <a class="result__a" href="...">Title</a>
|
||||||
// The previous regex was a bit strict. Let's make it more flexible for attributes order/content
|
// The previous regex was a bit strict. Let's make it more flexible for attributes order/content
|
||||||
reLink := regexp.MustCompile(`<a[^>]*class="[^"]*result__a[^"]*"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)</a>`)
|
matches := reDDGLink.FindAllStringSubmatch(html, count+5)
|
||||||
matches := reLink.FindAllStringSubmatch(html, count+5)
|
|
||||||
|
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
return fmt.Sprintf("No results found or extraction failed. Query: %s", query), nil
|
return fmt.Sprintf("No results found or extraction failed. Query: %s", query), nil
|
||||||
|
|
@ -136,8 +148,7 @@ func (p *DuckDuckGoSearchProvider) extractResults(html string, count int, query
|
||||||
|
|
||||||
// A better regex approach: iterate through text and find matches in order
|
// A better regex approach: iterate through text and find matches in order
|
||||||
// But for now, let's grab all snippets too
|
// But for now, let's grab all snippets too
|
||||||
reSnippet := regexp.MustCompile(`<a class="result__snippet[^"]*".*?>([\s\S]*?)</a>`)
|
snippetMatches := reDDGSnippet.FindAllStringSubmatch(html, count+5)
|
||||||
snippetMatches := reSnippet.FindAllStringSubmatch(html, count+5)
|
|
||||||
|
|
||||||
maxItems := min(len(matches), count)
|
maxItems := min(len(matches), count)
|
||||||
|
|
||||||
|
|
@ -172,8 +183,7 @@ func (p *DuckDuckGoSearchProvider) extractResults(html string, count int, query
|
||||||
}
|
}
|
||||||
|
|
||||||
func stripTags(content string) string {
|
func stripTags(content string) string {
|
||||||
re := regexp.MustCompile(`<[^>]+>`)
|
return reTags.ReplaceAllString(content, "")
|
||||||
return re.ReplaceAllString(content, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PerplexitySearchProvider struct {
|
type PerplexitySearchProvider struct {
|
||||||
|
|
@ -483,19 +493,14 @@ func (t *WebFetchTool) Execute(ctx context.Context, args map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *WebFetchTool) extractText(htmlContent string) string {
|
func (t *WebFetchTool) extractText(htmlContent string) string {
|
||||||
re := regexp.MustCompile(`<script[\s\S]*?</script>`)
|
result := reScript.ReplaceAllLiteralString(htmlContent, "")
|
||||||
result := re.ReplaceAllLiteralString(htmlContent, "")
|
result = reStyle.ReplaceAllLiteralString(result, "")
|
||||||
re = regexp.MustCompile(`<style[\s\S]*?</style>`)
|
result = reTags.ReplaceAllLiteralString(result, "")
|
||||||
result = re.ReplaceAllLiteralString(result, "")
|
|
||||||
re = regexp.MustCompile(`<[^>]+>`)
|
|
||||||
result = re.ReplaceAllLiteralString(result, "")
|
|
||||||
|
|
||||||
result = strings.TrimSpace(result)
|
result = strings.TrimSpace(result)
|
||||||
|
|
||||||
re = regexp.MustCompile(`[^\S\n]+`)
|
result = reWhitespace.ReplaceAllString(result, " ")
|
||||||
result = re.ReplaceAllString(result, " ")
|
result = reBlankLines.ReplaceAllString(result, "\n\n")
|
||||||
re = regexp.MustCompile(`\n{3,}`)
|
|
||||||
result = re.ReplaceAllString(result, "\n\n")
|
|
||||||
|
|
||||||
lines := strings.Split(result, "\n")
|
lines := strings.Split(result, "\n")
|
||||||
var cleanLines []string
|
var cleanLines []string
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue