Skip to content

fix(dspy): accept LangChain tools with JSON schema args - #10621

Open
zouyonghe wants to merge 4 commits into
stanfordnlp:mainfrom
zouyonghe:fix/langchain-json-schema
Open

zouyonghe wants to merge 4 commits into
stanfordnlp:mainfrom
zouyonghe:fix/langchain-json-schema

Conversation

@zouyonghe

Copy link
Copy Markdown

📝 Changes Description

Fix dspy.Tool.from_langchain() for LangChain tools whose args_schema is a JSON schema dictionary.

LangChain officially supports dictionary schemas. Such a tool executes normally, but DSPy called model_json_schema() unconditionally and raised AttributeError. This patch sends dictionary schemas through existing convert_input_schema_to_tool_args() while preserving the Pydantic model path and its type annotations.

Verification

  • Baseline: 1 new failure / 2 existing tests passed; failure was dict missing model_json_schema.
  • Fixed: pytest tests/utils/test_langchain_tool.py tests/adapters/test_tool.py -q --extra → 42 passed.
  • Ruff, repository pre-commit hooks on changed files, and diff check passed.
  • Regression covers real LangChain StructuredTool, native schema required fields, invalid input rejection, and no underlying call after rejection.

AI assistance used for investigation, implementation, tests, and review. No live model/provider call; no full repository or cross-platform suite. Maintainer edits enabled.

@greptile-apps

greptile-apps Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium impact] No outstanding findings require changes before merging.

Summary

This PR lets dspy.Tool.from_langchain() accept JSON schema dictionaries while keeping Pydantic model annotations.

  • DSPy tools now accept LangChain JSON schema dictionaries.
  • Docs now use pymdown-extensions 12.1.

Reviews (4) · Last reviewed commit: "fix(dspy): pin compatible pymdown for do..." · Reviewed by Greptile

Comment thread dspy/utils/langchain_tool.py
for key, field in args_schema.model_fields.items()
}
if isinstance(args_schema, dict):
args, arg_types, arg_desc = convert_input_schema_to_tool_args(args_schema)

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.

P1 Required arguments change

The dictionary path does not preserve the schema's required list when advertising the tool. With required: ["query"] and an optional limit without a default, format_as_litellm_function_call() advertises both arguments as required. A required property with a default is advertised as optional instead. Both cases reproduce with a real StructuredTool: query-only input works despite the advertised requirement for limit, while an empty input accepted by the advertised schema fails execution because query is missing. Preserve the explicit required list and test both cases before merging; the new test only covers defaults that happen to match requiredness.

Artifacts

Evidence from the check

  • Preserved the full authored and executed StructuredTool harness locally; remote upload is blocked by the absence of an upload capability.

Command output from the check

  • Preserved the captured parent execution showing successful LangChain calls and dictionary-schema AttributeError; remote upload remains blocked.

Command output from the check

  • Preserved the captured head execution showing the nullable-schema TypeError and successful scalar control; remote upload remains blocked.

Evidence from the check

  • The previously executed Python harness remains at this local path; remote upload cannot be confirmed because no upload capability is available.

Evidence from the check

  • The previously executed shell capture script remains at this local path; remote upload cannot be confirmed because no upload capability is available.

Command output from the check

  • The captured parent execution records both dictionary conversion failures and source-contract checks; the observed evidence is preserved locally.

Command output from the check

  • The captured head execution records both required-field mismatches and validation and invocation results; the observed evidence is preserved locally.

View artifacts

T-Rex Ran code and verified through T-Rex

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Confirmed both requiredness mismatches independently. dc14339 preserves a detached copy of the original dict schema for native advertisement and whole-argument validation. required-with-default stays required; optional-without-default stays optional; missing required and forbidden additional properties reject before ainvoke. Existing function/Pydantic schema inference stays unchanged. Regression asserts exact advertised schema and successful invocation only for valid arguments. Full corrective diff reviewed before push; remote acceptance still pending.

@zouyonghe

Copy link
Copy Markdown
Author

Follow-up verification at 4468587: tested the declared LangChain Core 0.3.0 floor as well as 1.6.9. Core 0.3.0 cannot construct dict-schema StructuredTool instances, so the initial new test incorrectly assumed a newer SDK feature. The test-only follow-up keeps bridge-level dict-schema coverage on all versions and retains a separate real StructuredTool execution test, skipping only the known old-SDK construction TypeError. Production code is unchanged. Results: Core 0.3.0: 42 passed / 1 explicit unsupported-feature skip; Core 1.6.9: 43 passed. Relevant pre-commit hooks passed. Python 3.12.15, no providers/models called; not full-suite or all-version verification. Full follow-up diff reviewed before push.

@zouyonghe

Copy link
Copy Markdown
Author

Expanded offline regression on corrective head dc14339: after installing the repository-declared Pillow dev dependency, pytest tests/adapters tests/utils/test_langchain_tool.py -q --extra reports 381 passed / 1 existing Pydantic warning. Initial run was 380 passed / 1 missing-Pillow failure; no production code change was needed. This broadens shared Tool/adapter coverage beyond the focused 47-test run, but is not full-repository, provider, or cross-platform verification.

@zouyonghe

Copy link
Copy Markdown
Author

Fixed the docs build failure with a minimal docs-only dependency pin in 8cad034. Failure was reproducible: docs job installed pymdown-extensions 12.2 with mkdocstrings 1.0.6; Highlighter construction raised Highlight.__init__() missing ... md. The same base branch successful docs run used 12.1. Local ROOT-isolated probe reproduces TypeError on 12.2 and successfully constructs/renders with 12.1. docs/requirements.txt now pins 12.1 with rationale. Source regression suite remains 47 passed; hooks pass. Python 3.10 job separately had 8 shared LiteLLM test-server startup timeouts (2396 passed, 340 skipped, 2 xfailed); server startup completed after the 60s readiness deadline, cause not established. I attempted rerun-failed but GitHub denied admin permission. No timeout changes or skips; new CI run needed to determine whether this repeats.

This branch has not been deployed

No deployments
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