Skip to content

Commit 4c87d2b

Browse files
committed
perf: update error hint
1 parent cf623a7 commit 4c87d2b

4 files changed

Lines changed: 51 additions & 17 deletions

File tree

errors.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ type ValidationIssue struct {
103103
Path string
104104
Expected string // for IssueType only
105105
Received string // for IssueType only
106+
Hint string // optional fix hint, appended to the rendered message
106107
}
107108

108109
const (
@@ -235,15 +236,22 @@ func formatValidationIssues(toolName string, issues []ValidationIssue) string {
235236

236237
lines := make([]string, 0, len(issues))
237238
for _, it := range issues {
239+
var line string
238240
switch it.Kind {
239241
case IssueMissing:
240-
lines = append(lines, fmt.Sprintf("The required parameter `%s` is missing", it.Path))
242+
line = fmt.Sprintf("The required parameter `%s` is missing", it.Path)
241243
case IssueType:
242-
lines = append(lines, fmt.Sprintf(
244+
line = fmt.Sprintf(
243245
"The parameter `%s` type is expected as `%s` but provided as `%s`",
244246
it.Path, it.Expected, it.Received,
245-
))
247+
)
248+
default:
249+
continue
246250
}
251+
if it.Hint != "" {
252+
line += ". " + it.Hint
253+
}
254+
lines = append(lines, line)
247255
}
248256

249257
noun := "issue"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ module github.com/voocel/agentcore
33
go 1.25.0
44

55
require (
6-
github.com/voocel/litellm v1.6.12
6+
github.com/voocel/litellm v1.6.13
77
golang.org/x/image v0.41.0
88
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
github.com/voocel/litellm v1.6.12 h1:C/JP/F6wKtN6t9c23CGIX9sSAQMKs4UCuUz3jd/mxsA=
2-
github.com/voocel/litellm v1.6.12/go.mod h1:6MBUu3I4DHm7h72Vl+3nqLruSwYmgqMf/I9BGoordJ4=
1+
github.com/voocel/litellm v1.6.13 h1:lig/RSTbZZBquatPCbO1G0WGEaQ1pt+Ra7sAVCG0QuA=
2+
github.com/voocel/litellm v1.6.13/go.mod h1:6MBUu3I4DHm7h72Vl+3nqLruSwYmgqMf/I9BGoordJ4=
33
golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo=
44
golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA=

loop.go

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,12 +1149,13 @@ func validateToolArgs(tool Tool, call ToolCall) error {
11491149
if expectedType == "" {
11501150
continue
11511151
}
1152-
if received, mismatch := typeMismatch(val, expectedType); mismatch {
1152+
if received, hint, mismatch := typeMismatch(val, expectedType); mismatch {
11531153
issues = append(issues, ValidationIssue{
11541154
Kind: IssueType,
11551155
Path: key,
11561156
Expected: expectedType,
11571157
Received: received,
1158+
Hint: hint,
11581159
})
11591160
}
11601161
}
@@ -1168,37 +1169,62 @@ func validateToolArgs(tool Tool, call ToolCall) error {
11681169

11691170
// typeMismatch reports whether val conflicts with the JSON Schema type. When it
11701171
// does, the returned name is the user-facing JSON type name of the actual value
1171-
// ("string" / "integer" / "number" / "boolean" / "array" / "object").
1172-
func typeMismatch(val any, expected string) (string, bool) {
1172+
// ("string" / "integer" / "number" / "boolean" / "array" / "object"). The hint
1173+
// is non-empty only for known LLM mistake patterns — currently the
1174+
// "JSON-encoded literal passed as a string" mistake that weaker models repeat
1175+
// across turns without nudging.
1176+
func typeMismatch(val any, expected string) (received string, hint string, mismatch bool) {
11731177
switch expected {
11741178
case "string":
11751179
if _, ok := val.(string); ok {
1176-
return "", false
1180+
return "", "", false
11771181
}
11781182
case "integer":
11791183
if f, ok := val.(float64); ok && f == float64(int64(f)) {
1180-
return "", false
1184+
return "", "", false
11811185
}
11821186
case "number":
11831187
if _, ok := val.(float64); ok {
1184-
return "", false
1188+
return "", "", false
11851189
}
11861190
case "boolean":
11871191
if _, ok := val.(bool); ok {
1188-
return "", false
1192+
return "", "", false
11891193
}
11901194
case "array":
11911195
if _, ok := val.([]any); ok {
1192-
return "", false
1196+
return "", "", false
11931197
}
11941198
case "object":
11951199
if _, ok := val.(map[string]any); ok {
1196-
return "", false
1200+
return "", "", false
11971201
}
11981202
default:
1199-
return "", false
1203+
return "", "", false
12001204
}
1201-
return jsonTypeName(val), true
1205+
return jsonTypeName(val), mismatchHint(val, expected), true
1206+
}
1207+
1208+
// mismatchHint catches the single most common LLM mistake: serializing an
1209+
// array/object to a JSON string and passing the string. The hint nudges the
1210+
// model to drop the surrounding quotes on the next turn. Other mismatches get
1211+
// no hint to keep the error message terse.
1212+
func mismatchHint(val any, expected string) string {
1213+
if expected != "array" && expected != "object" {
1214+
return ""
1215+
}
1216+
s, ok := val.(string)
1217+
if !ok {
1218+
return ""
1219+
}
1220+
trimmed := strings.TrimSpace(s)
1221+
if expected == "array" && strings.HasPrefix(trimmed, "[") {
1222+
return `Looks like a JSON-encoded array — pass the value directly (e.g. ["a","b"]), not wrapped in quotes.`
1223+
}
1224+
if expected == "object" && strings.HasPrefix(trimmed, "{") {
1225+
return `Looks like a JSON-encoded object — pass the value directly (e.g. {"k":"v"}), not wrapped in quotes.`
1226+
}
1227+
return ""
12021228
}
12031229

12041230
// jsonTypeName returns the JSON Schema type name for val.

0 commit comments

Comments
 (0)