Skip to content

fix(dspy): preserve schemas for partial callables - #10617

Open
zouyonghe wants to merge 1 commit into
stanfordnlp:mainfrom
zouyonghe:fix/tool-partial-schema
Open

zouyonghe wants to merge 1 commit into
stanfordnlp:mainfrom
zouyonghe:fix/tool-partial-schema

Conversation

@zouyonghe

Copy link
Copy Markdown

📝 Changes Description

Fix dspy.Tool schema inference for functools.partial callables.

Prebinding context is useful when exposing a Python function as an agent tool. Currently, Tool(partial(lookup, "demo")) inspects the generic partial.__call__ method and advertises required args / kwargs instead of the remaining typed arguments. Native function-calling adapters pass that incorrect schema to the model even though the original partial can execute correctly.

This change reads the effective signature from the partial and the type hints from its underlying function, bound method, or callable instance. Positional bindings disappear from the schema; keyword bindings remain overridable defaults, matching Python semantics. Existing names/descriptions and explicit schema overrides are unchanged. No dependencies or public options added.

Minimal baseline example:

from functools import partial
import dspy

def lookup(tenant: str, record_id: int) -> str:
    return f"{tenant}:{record_id}"

tool = dspy.Tool(partial(lookup, "demo"), name="lookup")
print(tool.args)
# Before: {'args': {}, 'kwargs': {}}
# After: {'record_id': {'type': 'integer'}}

Verification

  • Five new regressions failed on the original parser, including the native JSONAdapter formatting path. Controls cover positional/keyword binding, override/default behavior, nested partials, bound methods, callable instances, and invalid argument rejection.
  • Python 3.12.15, reviewed head f2267837922811fc3b2bc1ba0f3056ae3e58e146:
    python -m pytest tests/adapters/test_tool.py tests/adapters/test_json_adapter.py -q -k 'not formats_with_nested_documents': 82 passed / 1 deselected.
  • The full two-file run has one existing failure, test_json_adapter_formats_with_nested_documents, because dspy.experimental is not exposed. Reverting only the parser fix reproduces that same failure alongside all five new failures. One existing Pydantic ReadOnly warning remains.
  • Ruff 0.15.0 lint and repository pre-commit hooks for both changed files passed.
  • Pre-submission five-axis self-review found no blocking issue in this scope. Not a maintainer approval or full-suite verification.

✅ Contributor Checklist

  • Relevant pre-commit checks pass locally. Remote checks pending.
  • Conventional PR title and commit message.
  • Reviewed full diff before submission; regressions verified red→green.

⚠️ Warnings

AI assistance was used for investigation, implementation, tests, and self-review. No live model/provider calls were made. Full repository tests, cross-platform execution, and non-introspectable builtin callables were not verified. This does not turn prebound keyword arguments into an authorization boundary; they remain overridable as Python permits.

@greptile-apps

greptile-apps Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium impact] Safe to merge within the reviewed scope; focused schema, native adapter, validation, and invocation checks found no blocking issue.

T-Rex evidence

Evidence from the check

  • This executed Python script compares base and PR inference through the native JSONAdapter boundary without invoking wrapped callables, making the checks reproducible.

Evidence from the check

  • This executed shell script runs both comparisons with the existing virtual environment and captures their commands, exit codes, and output, preserving the exact execution procedure.

Command output from the check

  • The base inference method was executed against the same schema assertions and native adapter tests, showing generic args/kwargs properties instead of the remaining typed parameters.

Command output from the check

  • The PR code was executed against the same checks, showing all 10 schema cases and four focused tests passing with the expected remaining parameters and defaults.

Evidence from the check

  • The executed harness runs selected existing tests and identical before/after Tool invocation assertions, making the comparison reproducible.

Evidence from the check

  • The executed runner sets LITELLM_LOCAL_MODEL_COST_MAP=True and records the command, working directory, exit code, stdout, and stderr, preserving observed evidence.

Command output from the check

  • Ran the selected existing tests and actual Tool calls on head; all 18 tests and 190 runtime assertions passed, with defaults and invalid-argument rejection working.

Command output from the check

  • Ran the identical checks with the exact base inference method substituted in memory; all non-partial controls passed while partial validation checks exposed the pre-change gaps.

View artifacts

Summary

Fixes dspy.Tool schema inference for functools.partial.

  • Tools made from partials show the arguments callers can still pass.

Acknowledged limitations:

  • zouyonghe states that prebound keyword arguments intentionally remain overridable under Python semantics and are not an authorization boundary.
  • zouyonghe identifies an existing test_json_adapter_formats_with_nested_documents failure because dspy.experimental is not exposed, and reports that reverting only the parser fix reproduces the same failure. This acknowledged existing issue is excluded from the findings.
  • zouyonghe notes that full repository tests, cross-platform execution, and non-introspectable builtin callables were not verified. The focused checks do not establish coverage for those areas.

Reviews (1) · Last reviewed commit: "fix(dspy): preserve schemas for partial ..." · Reviewed by Greptile

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