mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-16 06:05:07 +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 {
|
func findQuotedFunctionCallKeyStart(s string) int {
|
||||||
lower := strings.ToLower(s)
|
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); {
|
for from := 0; from < len(lower); {
|
||||||
rel := strings.Index(lower[from:], key)
|
rel := strings.Index(lower[from:], key)
|
||||||
if rel < 0 {
|
if rel < 0 {
|
||||||
@@ -15,6 +30,10 @@ func findQuotedFunctionCallKeyStart(s string) int {
|
|||||||
from = idx + 1
|
from = idx + 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !hasJSONKeyBoundary(lower, idx, len(key)) {
|
||||||
|
from = idx + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
j := idx + len(key)
|
j := idx + len(key)
|
||||||
for j < len(lower) && (lower[j] == ' ' || lower[j] == '\t' || lower[j] == '\r' || lower[j] == '\n') {
|
for j < len(lower) && (lower[j] == ' ' || lower[j] == '\t' || lower[j] == '\r' || lower[j] == '\n') {
|
||||||
j++
|
j++
|
||||||
@@ -30,3 +49,23 @@ func findQuotedFunctionCallKeyStart(s string) int {
|
|||||||
func hasJSONObjectContextPrefix(prefix string) bool {
|
func hasJSONObjectContextPrefix(prefix string) bool {
|
||||||
return strings.LastIndex(prefix, "{") >= 0
|
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) {
|
func TestProcessToolSieveDoesNotBufferFunctionCallProse(t *testing.T) {
|
||||||
var state toolStreamSieveState
|
var state toolStreamSieveState
|
||||||
chunk := "Please explain the functionCall API field and keep streaming this sentence."
|
chunk := "Please explain the functionCall API field and keep streaming this sentence."
|
||||||
|
|||||||
Reference in New Issue
Block a user