Fix tool loop handling and count tokens endpoint

This commit is contained in:
lutc5
2026-05-06 15:58:37 +08:00
parent 1c349227a3
commit fe1d5b5348
6 changed files with 182 additions and 7 deletions

View File

@@ -283,10 +283,11 @@ func ActionOutputPrompt(toolCallID string, output string) string {
if output == "" {
return ""
}
next := "Based on the tool result above, answer the user's request directly if you have enough information. Only use another structured action block if a specific missing fact still requires another tool call."
if id := strings.TrimSpace(toolCallID); id != "" {
return "Tool result for " + id + ":\n" + output + "\n\nBased on the tool result above, continue with the next appropriate action using the structured format."
return "Tool result for " + id + ":\n" + output + "\n\n" + next
}
return "Tool result:\n" + output + "\n\nBased on the tool result above, continue with the next appropriate action using the structured format."
return "Tool result:\n" + output + "\n\n" + next
}
func ActionBlockExample(tools []ToolDef) string {
@@ -629,6 +630,9 @@ func ParseActionBlocks(text string, tools []ToolDef, cfg Config) ([]ToolCall, st
// Filter arguments against the tool's input schema to strip unknown params
if schema, ok := toolSchemaMap[call.Name]; ok && len(schema) > 0 {
call.Arguments = filterArgsBySchema(call.Arguments, schema)
if !hasRequiredArgs(call.Arguments, schema) {
continue
}
}
calls = append(calls, call)
spans = append(spans, span{start: start, end: end + 3})
@@ -1011,6 +1015,19 @@ func filterArgsBySchema(args map[string]any, schema map[string]any) map[string]a
return out
}
func hasRequiredArgs(args map[string]any, schema map[string]any) bool {
for _, key := range requiredKeys(schema) {
value, ok := args[key]
if !ok {
return false
}
if s, ok := value.(string); ok && strings.TrimSpace(s) == "" {
return false
}
}
return true
}
func cloneMap(src map[string]any) map[string]any {
if src == nil {
return nil

View File

@@ -154,3 +154,25 @@ func TestParseActionBlocksMapsReadAlias(t *testing.T) {
t.Fatalf("calls = %+v", calls)
}
}
func TestParseActionBlocksDropsCallsMissingRequiredArgs(t *testing.T) {
text := "```json action\n{\"tool\":\"Read\",\"parameters\":{\"path\":\"/tmp/a.txt\"}}\n```"
calls, clean, err := ParseActionBlocks(text, []ToolDef{{
Name: "Read",
InputSchema: map[string]any{
"properties": map[string]any{
"file_path": map[string]any{"type": "string"},
},
"required": []any{"file_path"},
},
}}, Config{})
if err != nil {
t.Fatal(err)
}
if len(calls) != 0 {
t.Fatalf("expected no calls, got %+v", calls)
}
if !strings.Contains(clean, "\"path\"") {
t.Fatalf("clean should preserve unparseable action block, got %q", clean)
}
}