Skip to content

fix(dspy): unify OpenAI reasoning-model classifier for non-openai provider prefixes - #89

Open
detail-app[bot] wants to merge 2 commits into
mainfrom
detail/bug-fix/fix-dspy-unify-openai-reasoning-model-classifier-f-21690d
Open

fix(dspy): unify OpenAI reasoning-model classifier for non-openai provider prefixes#89
detail-app[bot] wants to merge 2 commits into
mainfrom
detail/bug-fix/fix-dspy-unify-openai-reasoning-model-classifier-f-21690d

Conversation

@detail-app

@detail-app detail-app Bot commented Sep 6, 2026

Copy link
Copy Markdown

Warning

GitHub issue creation failed

Detail attempted to publish this bug to GitHub, but the issue could not be created. This fix PR was created without that issue, and missing tracker references are shown as Unknown issue.

You can review and merge this PR normally. Please review your tracker integration settings before the next publish run.

Detail bug report: View on Detail

📝 Changes Description

Closes Unknown issue

Bug
dspy/clients/openai_format.py and dspy/clients/lm.py each defined their own _is_openai_reasoning_model, and the two copies diverged:

  • openai_format.py stripped only the literal openai/ prefix (removeprefix) and used a loose startswith.
  • lm.py stripped any provider prefix (split("/")[-1]) and used an anchored regex.

For non-openai/ prefixes like azure/o3, the adapter-path classifier misclassified the model as non-reasoning. On the standard dspy.Predict adapter path this caused two symptoms for azure/o3 (while openai/o3 behaved correctly):

  1. max_tokens set as a Predictor-level kwarg was sent under max_tokens instead of max_completion_tokens.
  2. The client-side temperature + reasoning_effort validation was skipped, letting invalid combinations reach the provider instead of raising LMUnsupportedFeatureError.

Fix
Extracted the classifier into a shared neutral module dspy/clients/_openai_model_family.py (using lm.py's stricter grammar — strip any provider prefix + anchored regex). Both lm.py and openai_format.py now import and call the same is_openai_reasoning_model, so future drift is a single definition, not two. A neutral module is required because lm.py already imports from openai_format.py, so a direct reverse import would be circular.

Testing

  • New unit tests in tests/clients/test_lm.py: a single-source-of-truth guard (both modules reference the same function object), a parametrized classifier test covering provider-prefix parity and the strict-grammar footgun (e.g. azure/o3-preview, azure/o1-mini-pro rejected), and an adapter-path test that captures the litellm_completion request and asserts max_completion_tokens for both azure/o3 and openai/o3. Existing test_reasoning_model_token_parameter and test_reasoning_model_requirements were extended with azure cases.
  • Routine checks pass: ruff check (CI-strict), pre-commit hooks, and the full default CI matrix pytest -m 'not extra and not deno' tests/ (1287 passed). No regressions across tests/clients/, tests/adapters/, tests/predict/.
  • End-to-end (mocked, network-free): reproduced the bug-report scenario before and after the fix; azure/o3 now matches openai/o3 on both the wire key and the validation gate.
  • Not verified: a live Azure OpenAI call to confirm the provider accepts max_completion_tokens could not be run — it blocked at credential acquisition with openai.OpenAIError: Missing credentials. Please pass one of api_key, azure_ad_token, azure_ad_token_provider, or the AZURE_OPENAI_API_KEY or AZURE_OPENAI_AD_TOKEN environment variables. (no Azure credentials in this environment). The request did reach litellm's Azure o_series_handler before failing on auth, confirming the wire path is correct.

✅ Contributor Checklist

  • Pre-Commit checks are passing (locally and remotely)
  • Title of your PR / MR corresponds to the required format
  • Commit message follows required format {label}(dspy): {message}

⚠️ Warnings

Authored by Detail (AI-assisted). The fix was validated by reproducing the reported divergence, unifying the classifier, and running the test matrix above; every line is understood.


Automatic Fixes PRs can be configured here.

@greptile-apps

greptile-apps Bot commented Sep 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR centralizes OpenAI reasoning-model detection so LM configuration and OpenAI request formatting consistently handle provider-prefixed model identifiers.

  • Introduces a shared model-family classifier with provider-prefix normalization.
  • Uses the classifier for token-field selection, validation, and LM serialization.
  • Adds regression coverage for Azure-prefixed reasoning models.
  • The change since the previous review restores classification of o1-preview.

Confidence Score: 5/5

The PR appears safe to merge, with the previously reported o1-preview classification issue fixed and no new actionable failures identified.

The shared classifier now recognizes o1-preview, while both LM configuration and request formatting use that same function; the previous thread was also manually resolved.

Important Files Changed

Filename Overview
dspy/clients/_openai_model_family.py Defines the shared provider-prefix-aware reasoning-model classifier and now preserves the previously missed o1-preview model.
dspy/clients/lm.py Replaces the local classifier with the shared implementation across configuration and serialization paths.
dspy/clients/openai_format.py Uses the shared classifier for reasoning-temperature validation and completion-token field selection.
tests/clients/test_lm.py Adds classifier identity, provider-prefix parity, strict grammar, and adapter request regressions.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[Provider-prefixed model ID] --> B[Shared reasoning-model classifier]
    B --> C[LM configuration validation]
    B --> D[OpenAI request formatter]
    C --> E[Reasoning-compatible defaults]
    D --> F[max_completion_tokens]
    F --> G[LiteLLM provider request]
Loading

Reviews (2): Last reviewed commit: "fix: preserve o1-preview in shared reaso..." | Re-trigger Greptile

Comment thread dspy/clients/_openai_model_family.py
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