Add native structured-output ("provider strategy") for DataExtractionChain, on by default - #612
Open
Chuukwudi wants to merge 3 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
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
:strategyoption with default behavior of trying:provider_strategyfirst and falling back to:tool_strategywhen unsupported. - Introduce
supports_provider_strategy?/1and provider-strategy LLM patching (json_response,json_schema, and extra defaults likejson_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.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DataExtractionChaincurrently extracts entities by asking the model to call aninformation_extractiontool. 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 publicrun/4/run_chain/4contract is unchanged.Why
langchainlibrary explicitly recommends structured output over tool calling for extraction-style tasks — see Structured output. This PR bringselixir-langchain'sDataExtractionChainin line with that guidance/precedent.What changed
:strategyoption onrun/4/run_chain/4::provider_strategyfirst. If the givenllm's struct type doesn't support it (checked via the newsupports_provider_strategy?/1), it fails gracefully and falls back to:tool_strategy, logging aLogger.warning/1explaining why.strategy: :provider_strategyagainst a model that can't do structured output raises, rather than silently doing something else.strategy: :tool_strategyalways 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_schemaand:json_responsefields — i.e. whether it's structurally capable of a structured-output request at all.:provider_strategy, the chain patches a copy of the givenllmwithjson_response: trueandjson_schemaset 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.ChatOpenAIResponsesrequires a separate:json_schema_name).extract_result/1now 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
run/4/run_chain/4signatures 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.Caveats
response_formatnested 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