Skip to content

Add native structured-output ("provider strategy") for DataExtractionChain, on by default - #612

Open
Chuukwudi wants to merge 3 commits into
brainlid:mainfrom
Chuukwudi:entity-extraction-without-tool-calls
Open

Add native structured-output ("provider strategy") for DataExtractionChain, on by default#612
Chuukwudi wants to merge 3 commits into
brainlid:mainfrom
Chuukwudi:entity-extraction-without-tool-calls

Conversation

@Chuukwudi

@Chuukwudi Chuukwudi commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

DataExtractionChain currently extracts entities by asking the model to call an information_extraction tool. This works, but it isn't the cheapest or most reliable option anymore: most providers now support structured outputs — constraining the response to a JSON schema natively, without any tool/function calling.

This PR adds that as a first-class strategy (:provider_strategy), makes it the default, and keeps the original tool-calling behavior (:tool_strategy) as an automatic and explicit fallback. No existing call sites break — the public run/4 / run_chain/4 contract is unchanged.

Why

  • Lower token cost. Tool-calling requires sending a full JSON-Schema tool/function definition with every request, on top of the completion. A single extraction schema might look small, but on a per-request basis across a high-volume extraction workload, that repeated definition adds up quickly in prompt tokens. Structured output mode skips this entirely — the schema constrains decoding instead of being sent as a callable tool, so we pay for it once as configuration, not per-token in the prompt.
  • Fewer failure modes. Tool calling can produce dangling/partial tool calls, wrong argument shapes, or the model choosing not to call the tool at all. Structured output constrains the token-level decoding itself, so it's generally more reliable for pure data-shaping tasks like this.
  • Matches upstream LangChain's own guidance. The Python langchain library explicitly recommends structured output over tool calling for extraction-style tasks — see Structured output. This PR brings elixir-langchain's DataExtractionChain in line with that guidance/precedent.

What changed

  • New :strategy option on run/4 / run_chain/4:
    • Not given (default): tries :provider_strategy first. If the given llm's struct type doesn't support it (checked via the new supports_provider_strategy?/1), it fails gracefully and falls back to :tool_strategy, logging a Logger.warning/1 explaining why.
    • Given explicitly: used strictly, no fallback. strategy: :provider_strategy against a model that can't do structured output raises, rather than silently doing something else. strategy: :tool_strategy always uses tool calling.
  • supports_provider_strategy?/1 (new, public): checks whether a chat model's struct type (not just the current instance's configured values) defines both :json_schema and :json_response fields — i.e. whether it's structurally capable of a structured-output request at all.
  • Under :provider_strategy, the chain patches a copy of the given llm with json_response: true and json_schema set to the schema exactly as passed in (no wrapping), filling in sensible defaults for the few extra fields some providers need alongside the schema (e.g. ChatOpenAIResponses requires a separate :json_schema_name).
  • extract_result/1 now also accepts a plain JSON response (list or map) in addition to the tool-call shape, so it works for whatever schema you pass in.

Compatibility

  • The run/4 / run_chain/4 signatures are unchanged — this is opt-out (strategy: :tool_strategy), not opt-in, everywhere except behavior: previously every call went through tool calling; now calls against models that support structured output (OpenAI, Anthropic, Google, Mistral, DeepSeek, Bedrock/AWS Mantle, etc.) will use it automatically unless told otherwise.
  • Models without native structured-output support (e.g. Grok) fall back to tool calling automatically, with a warning logged.

Caveats

  • The wire format for structured output isn't fully uniform across providers (e.g. Mistral expects the entire response_format nested under :json_schema, unlike Anthropic/OpenAI). I believe that there is a mistake in the mistral implementation. Fixing it will lead to a breaking change and I have raised an issue here and the PR here

Copilot AI lite review requested due to automatic review settings August 7, 2026 08:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates LangChain.Chains.DataExtractionChain to prefer provider-native structured outputs (“provider strategy”) for JSON-schema constrained extraction, while preserving the existing tool-calling extraction (“tool strategy”) as an explicit option and as an automatic fallback when structured outputs aren’t supported by a given chat model.

Changes:

  • Add :strategy option with default behavior of trying :provider_strategy first and falling back to :tool_strategy when unsupported.
  • Introduce supports_provider_strategy?/1 and provider-strategy LLM patching (json_response, json_schema, and extra defaults like json_schema_name).
  • Expand result extraction to support plain JSON (map/list) responses and add tests covering provider strategy, fallback behavior, and strict strategy validation.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
lib/chains/data_extraction_chain.ex Implements provider-strategy execution path, strategy selection/fallback logic, capability probing, and JSON-response extraction.
test/chains/data_extraction_chain_test.exs Adds coverage for provider strategy defaulting, strict strategy behavior, capability checks, and fallback logging.
test/test_helper.exs Enables Mimic for ChatOpenAIResponses to support new provider-strategy tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/chains/data_extraction_chain.ex Outdated
Comment thread lib/chains/data_extraction_chain.ex Outdated
Comment thread lib/chains/data_extraction_chain.ex
Comment thread lib/chains/data_extraction_chain.ex Outdated
Chuukwudi and others added 2 commits August 7, 2026 10:57
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.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.

2 participants