mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-04 00:15:28 +08:00
fix: detect loose functionCall keys in tool sieve
This commit is contained in:
@@ -4,7 +4,22 @@ import "strings"
|
||||
|
||||
func findQuotedFunctionCallKeyStart(s string) int {
|
||||
lower := strings.ToLower(s)
|
||||
const key = "\"functioncall\""
|
||||
quotedIdx := findFunctionCallKeyStart(lower, `"functioncall"`)
|
||||
baretIdx := findFunctionCallKeyStart(lower, "functioncall")
|
||||
|
||||
switch {
|
||||
case quotedIdx < 0:
|
||||
return baretIdx
|
||||
case baretIdx < 0:
|
||||
return quotedIdx
|
||||
case quotedIdx < baretIdx:
|
||||
return quotedIdx
|
||||
default:
|
||||
return baretIdx
|
||||
}
|
||||
}
|
||||
|
||||
func findFunctionCallKeyStart(lower, key string) int {
|
||||
for from := 0; from < len(lower); {
|
||||
rel := strings.Index(lower[from:], key)
|
||||
if rel < 0 {
|
||||
@@ -15,6 +30,10 @@ func findQuotedFunctionCallKeyStart(s string) int {
|
||||
from = idx + 1
|
||||
continue
|
||||
}
|
||||
if !hasJSONKeyBoundary(lower, idx, len(key)) {
|
||||
from = idx + 1
|
||||
continue
|
||||
}
|
||||
j := idx + len(key)
|
||||
for j < len(lower) && (lower[j] == ' ' || lower[j] == '\t' || lower[j] == '\r' || lower[j] == '\n') {
|
||||
j++
|
||||
@@ -30,3 +49,23 @@ func findQuotedFunctionCallKeyStart(s string) int {
|
||||
func hasJSONObjectContextPrefix(prefix string) bool {
|
||||
return strings.LastIndex(prefix, "{") >= 0
|
||||
}
|
||||
|
||||
func hasJSONKeyBoundary(s string, idx, keyLen int) bool {
|
||||
if idx > 0 {
|
||||
prev := s[idx-1]
|
||||
if isLowerAlphaNumeric(prev) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if end := idx + keyLen; end < len(s) {
|
||||
next := s[end]
|
||||
if isLowerAlphaNumeric(next) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isLowerAlphaNumeric(b byte) bool {
|
||||
return (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9') || b == '_'
|
||||
}
|
||||
|
||||
@@ -135,6 +135,21 @@ func TestFindToolSegmentStartDetectsQuotedFunctionCallKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindToolSegmentStartDetectsLooseFunctionCallKey(t *testing.T) {
|
||||
input := `prefix {functionCall: {"name":"search_web","args":{"query":"x"}}}`
|
||||
want := strings.Index(input, "{")
|
||||
if got := findToolSegmentStart(input); got != want {
|
||||
t.Fatalf("expected JSON object start %d, got %d", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindToolSegmentStartIgnoresLooseFunctionCallProse(t *testing.T) {
|
||||
input := "Please explain why functionCall: is used in documentation examples."
|
||||
if got := findToolSegmentStart(input); got != -1 {
|
||||
t.Fatalf("expected no tool segment start for prose, got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessToolSieveDoesNotBufferFunctionCallProse(t *testing.T) {
|
||||
var state toolStreamSieveState
|
||||
chunk := "Please explain the functionCall API field and keep streaming this sentence."
|
||||
|
||||
Reference in New Issue
Block a user