functool's non-struct input wrapping ({"Arg0": ...}) breaks todo tools for models that don't guess the hidden envelope key

Aperta
#1,356 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@qmuntal ci sta già lavorando.

Dal 4/11/2026.

  • #1358 di @qmuntal — aperta

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
52/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
go

Direzione di ricerca

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.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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:

  1. Give the wrapper field a stable, descriptive JSON name (e.g. value or items) instead of the Go-reflection-derived Arg0, and document it so tool authors know what shape to describe to the model.
  2. For the specific agent/harness/todo provider, change todos_add/todos_complete/todos_remove to 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-go v0.3.0 and current main
  • Go 1.25
Lingua principale
Go
Stelle
659
Fork
67
Merge medio
14h 51m
PR unite (30g)
81

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di microsoft/agent-framework-go

Tutte le issue di microsoft/agent-framework-go

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.