functool's non-struct input wrapping ({"Arg0": ...}) breaks todo tools for models that don't guess the hidden envelope key
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 52/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- go
- Área
- ai, backend-api-design
Línea de trabajo
Start by reading functool.inputFormatFor and the related change in issue #527 to understand why non-struct inputs are wrapped. Then inspect the todo provider's todos_add, todos_complete, and todos_remove handlers under agent/harness/todo; compare their generated schemas and call behavior with the bare input shapes in this report. Done means the selected fix lets models call these tools using a discoverable input shape, with validation covering the reported cases.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
functool.inputFormatFor wraps any non-struct In type in an undocumented inputWrapper[T]{ Arg0 T } envelope, so the JSON schema exposed to the model for a tool like todos_remove(ids []int) requires {"Arg0": [...]} instead of a bare array. This key is never mentioned in the tool's name, description, or anywhere a model could discover it from context — it only exists because of the Go field name chosen for the wrapper struct.
In practice, several models (confirmed with a local/open model via an OpenAI-compatible endpoint; likely any model that isn't extremely strict about schema adherence) send the natural shape implied by the tool description instead — a bare array — and every call is rejected.
This directly breaks the built-in agent/harness/todo provider's todos_add, todos_complete, and todos_remove tools, which are defined with raw slice handlers:
func(ctx context.Context, input []ItemInput) ([]Item, error) // todos_add
func(ctx context.Context, items []CompleteInput) (int, error) // todos_complete
func(ctx context.Context, ids []int) (int, error) // todos_remove
When a model calls todos_remove with the bare array its own tool description implies, the call fails validation and the model has no way to discover the correct shape short of parsing the raw JSON schema character-by-character. In a real session, this left two todo items permanently stuck as "open" — the model repeatedly tried to mark them complete/remove them, got a rejection every time, and gave up, reporting it as a tool bug (it was right).
Repro
tl := functool.MustNew(
functool.Config{Name: "todos_remove", Description: "remove"},
func(ctx context.Context, ids []int) (int, error) {
return len(ids), nil
},
)
tl.Call(context.Background(), `[1,2]`)
// → validation error: validating root: type: [1 2] has type "array", want "object"
tl.Call(context.Background(), `{"Arg0":[1,2]}`)
// → 2, nil
Confirmed against both v0.3.0 and current main (2cd194d) — unchanged.
Related prior work
#527 ("Do not wrap pointer-to-struct functool inputs") fixed the same inputWrapper/Arg0 mechanism for the *Struct case, but by design left genuinely non-struct inputs (slices, maps, primitives) wrapped exactly as before. This issue is about that remaining case specifically causing real-world tool-call failures, not about removing the wrapping mechanism itself (which is presumably still needed to keep the top-level schema an object for non-struct types).
Suggested fix
A couple of options that don't require removing the wrapping mechanism:
- Give the wrapper field a stable, descriptive JSON name (e.g.
valueoritems) instead of the Go-reflection-derivedArg0, and document it so tool authors know what shape to describe to the model. - For the specific
agent/harness/todoprovider, changetodos_add/todos_complete/todos_removeto accept a small named struct (e.g.type RemoveInput struct { IDs []int }) instead of a raw slice, producing a flat, self-documenting schema ({"ids": [...]}) that doesn't depend on functool's wrapping behavior at all.
Happy to send a PR for option 2 (the todo provider specifically) if that's the preferred direction.
Environment
github.com/microsoft/agent-framework-gov0.3.0 and currentmain- Go 1.25
- Lenguaje dominante
- Go
- Estrellas
- 671
- Forks
- 68
- Merge medio
- 18 h 11 min
- PR fusionados (30 d)
- 82
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de microsoft/agent-framework-go
-
Add a per-run limit for tool callsPosiblemente ocupada @Lokesh118 la tomó hace 14 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 67/100
microsoft/agent-framework-go#1202 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 20/100
microsoft/agent-framework-go#1164 ·
Los mantenedores suelen responder en 1 día
-
[dotnet-port-api] Port TodoCompletion and BackgroundTaskCompletion loop evaluators (placement decision)Posiblemente ocupada @PratikDhanave la tomó hace 4 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 45/100
microsoft/agent-framework-go#1163 ·
Los mantenedores suelen responder en 1 día
-
[dotnet-port-api] Response.ModelID and ConversationID fieldsPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
microsoft/agent-framework-go#1140 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
microsoft/agent-framework-go#1139 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de microsoft/agent-framework-go
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
Los mantenedores suelen responder en 1 día
-
GitHub webhook is not subscribed to release events, so `event: release` never triggersPosiblemente ocupada @qsun la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
woodpecker-ci/woodpecker#7542 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
zilliztech/milvus-backup#1386 ·
Los mantenedores suelen responder en 1 día
-
Pipelined flush no longer observes kill signals after #2042: flush backoffer is built with nil varsAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día