101 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			101 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
package common
 | 
						||
 | 
						||
import (
 | 
						||
	"encoding/json"
 | 
						||
	"maps"
 | 
						||
	"regexp"
 | 
						||
	"slices"
 | 
						||
	"strings"
 | 
						||
	"unicode/utf8"
 | 
						||
)
 | 
						||
 | 
						||
// ToInterfaceSlice converts a slice of strings to a slice of empty interfaces.
 | 
						||
func ToInterfaceSlice[T any](slice []T) []interface{} {
 | 
						||
	interfaceSlice := make([]interface{}, len(slice))
 | 
						||
	for i, v := range slice {
 | 
						||
		interfaceSlice[i] = v
 | 
						||
	}
 | 
						||
	return interfaceSlice
 | 
						||
}
 | 
						||
 | 
						||
// []string -> string, " join, space separated
 | 
						||
func StringSliceJoin(slice []string) string {
 | 
						||
	result := make([]string, len(slice))
 | 
						||
	for i, v := range slice {
 | 
						||
		result[i] = `"` + v + `"`
 | 
						||
	}
 | 
						||
	return strings.Join(result, " ")
 | 
						||
}
 | 
						||
 | 
						||
func GetAttrs[A, B any](extract func(A) B, attrs ...A) []B {
 | 
						||
	result := make([]B, len(attrs))
 | 
						||
	for i, attr := range attrs {
 | 
						||
		result[i] = extract(attr)
 | 
						||
	}
 | 
						||
	return result
 | 
						||
}
 | 
						||
 | 
						||
// Deduplicate removes duplicates from a slice based on a key function
 | 
						||
// T: the type of elements in the slice
 | 
						||
// K: the type of key used for deduplication
 | 
						||
func Deduplicate[T any, K comparable](keyFunc func(T) K, items ...T) []T {
 | 
						||
	seen := make(map[K]T)
 | 
						||
	for _, item := range items {
 | 
						||
		key := keyFunc(item)
 | 
						||
		if _, exists := seen[key]; !exists {
 | 
						||
			seen[key] = item
 | 
						||
		}
 | 
						||
	}
 | 
						||
	return slices.Collect(maps.Values(seen))
 | 
						||
}
 | 
						||
 | 
						||
// ParseLLMJsonResponse parses a JSON response from LLM, handling cases where JSON is wrapped in code blocks.
 | 
						||
// This is useful when LLMs return responses like:
 | 
						||
// ```json
 | 
						||
// {"key": "value"}
 | 
						||
// ```
 | 
						||
// or regular JSON responses directly.
 | 
						||
func ParseLLMJsonResponse(content string, target interface{}) error {
 | 
						||
	// First, try to parse directly as JSON
 | 
						||
	err := json.Unmarshal([]byte(content), target)
 | 
						||
	if err == nil {
 | 
						||
		return nil
 | 
						||
	}
 | 
						||
 | 
						||
	// If direct parsing fails, try to extract JSON from code blocks
 | 
						||
	re := regexp.MustCompile("```(?:json)?\\s*([\\s\\S]*?)```")
 | 
						||
	matches := re.FindStringSubmatch(content)
 | 
						||
	if len(matches) >= 2 {
 | 
						||
		// Extract the JSON content within the code block
 | 
						||
		jsonContent := strings.TrimSpace(matches[1])
 | 
						||
		return json.Unmarshal([]byte(jsonContent), target)
 | 
						||
	}
 | 
						||
 | 
						||
	// If no code block found, return the original error
 | 
						||
	return err
 | 
						||
}
 | 
						||
 | 
						||
// CleanInvalidUTF8 移除字符串中的非法 UTF-8 字符和 \x00
 | 
						||
func CleanInvalidUTF8(s string) string {
 | 
						||
	var b strings.Builder
 | 
						||
	b.Grow(len(s))
 | 
						||
 | 
						||
	for i := 0; i < len(s); {
 | 
						||
		r, size := utf8.DecodeRuneInString(s[i:])
 | 
						||
		if r == utf8.RuneError && size == 1 {
 | 
						||
			// 非法 UTF-8 字节,跳过
 | 
						||
			i++
 | 
						||
			continue
 | 
						||
		}
 | 
						||
		if r == 0 {
 | 
						||
			// NULL 字符 \x00,跳过
 | 
						||
			i += size
 | 
						||
			continue
 | 
						||
		}
 | 
						||
		b.WriteRune(r)
 | 
						||
		i += size
 | 
						||
	}
 | 
						||
 | 
						||
	return b.String()
 | 
						||
}
 |