Skip to content

fix(langgraph): an enum output is not free text - #38

Open
MAabdelmoumen wants to merge 1 commit into
spichen:oi-forkfrom
MAabdelmoumen:fix/enum-output-is-not-free-text
Open

fix(langgraph): an enum output is not free text#38
MAabdelmoumen wants to merge 1 commit into
spichen:oi-forkfrom
MAabdelmoumen:fix/enum-output-is-not-free-text

Conversation

@MAabdelmoumen

Copy link
Copy Markdown

Problem

A single string output is treated as the agent's own free text: no structured generation is requested, and the output is filled from the final message. That keeps a plain text agent working on models without structured-output support.

An enum was caught by that rule, because an enum is a string. Nothing about the failure was visible:

  • no AgentOutputModel tool was ever bound, so nothing forced the model to answer structurally;
  • the model replied in prose;
  • the free-text fallback stored that whole sentence as the value.

So the declared options were never enforced. An agent with one enum output sentiment: positive | negative | neutral answered:

"Your sentiment seems very positive! Would you like to share more…"

and a BranchingNode keyed on that value matched none of its paths — raising KeyError: 'default' when no otherwise edge exists, or silently taking the fallback when one does.

Fix

An enum's schema names the only values the output may take, so free text can never satisfy it. It now takes the structured path, where the tool call is forced and the value is validated against the allowed set.

- return len(outputs) == 1 and outputs[0].type == "string"
+ if len(outputs) != 1 or outputs[0].type != "string":
+     return False
+ schema = outputs[0].json_schema or {}
+ return not schema.get("enum")

A plain string output keeps the free-text shortcut, unchanged.

Verification

  • the new test fails without the change (assert True is False) and passes with it
  • flows/test_agentnode.py, flows/test_managerworkers_node.py, flows/test_swarm_node.py, flows/test_branchingnode.py — no change in failure counts (the remaining failures need live LLM endpoints)
  • live, on a deployed runtime: a flow classifier declaring one enum output returns {"sentiment": "positive"} where it previously returned prose

Note

Only the enum case changes here. A plain single string field declared through a JSON output format still returns prose — whether that should honour the schema is a product decision, not a defect, so it is left alone.

A single string output is treated as the agent's own answer: no structured
generation is requested, and the output is filled from the final message so a
text agent still works on models without structured-output support.

An enum was caught by that rule, because an enum *is* a string. The consequences
were invisible: no `AgentOutputModel` tool was ever bound, the model replied in
prose, and the free-text fallback stored that whole sentence as the value — so
the declared options were never enforced. An agent with one enum output returned
sentences instead of one of its own values, and a `BranchingNode` keyed on it
matched none of its paths (raising `KeyError: 'default'` when no otherwise edge
exists, or silently taking the fallback when one does).

An enum's schema names the only values the output may take, so free text can
never satisfy it: it now takes the structured path, where the tool call is forced
and the value is validated against the allowed set. A plain string output keeps
the free-text shortcut unchanged.

Verified: the new test fails without the change, and a live flow whose classifier
declares one enum output now returns `{"sentiment": "positive"}` where it
previously returned prose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant