feat: add provider-agnostic withReasoning() request method#1018
Open
ChrisThompsonTLDR wants to merge 1 commit into
Open
feat: add provider-agnostic withReasoning() request method#1018ChrisThompsonTLDR wants to merge 1 commit into
ChrisThompsonTLDR wants to merge 1 commit into
Conversation
Introduces a `withReasoning(bool $enabled = true)` fluent setter on the
text and structured pending requests. Calling `->withReasoning(false)`
expresses the semantic intent of "skip reasoning" and is translated to
the right wire-format key by each provider:
| Provider | Wire format when disabled |
|------------|----------------------------------------|
| Ollama | `think: false` |
| OpenAI | `reasoning: { effort: "minimal" }` |
| Anthropic | omits the `thinking` block |
| Gemini | `thinkingConfig: { thinkingBudget: 0 }`|
| OpenRouter | `reasoning: { exclude: true }` |
| Perplexity | `reasoning_effort: "low"` |
Providers that do not support disabling reasoning treat the call as a
graceful no-op. Callers that have set the equivalent key explicitly via
`withProviderOptions()` continue to win, so the change is fully
backward compatible.
The new state lives in a `HasReasoning` concern that mirrors the shape
of `HasProviderOptions` and is composed onto both `PendingRequest` and
`Request` for text and structured generation.
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.
Description
Adds a
withReasoning(bool $enabled = true)fluent setter on text and structured pending requests so callers can express the semantic intent of "skip reasoning" once and have each provider translate it to the right wire-format key.Why
Each provider that supports reasoning / thinking exposes it through a different field today:
think: true|falsereasoning: { effort: "minimal"|"low"|... }thinking: { type: "enabled", budget_tokens: ... }(and simply omits the block when disabled)thinkingConfig: { thinkingBudget: 0|... }reasoning: { exclude: true|... }reasoning_effort: "low"|...To disable hidden chain-of-thought today, a caller has to know each provider's wire format and hand-roll it via
withProviderOptions(). Code that wants to be portable across providers (e.g. an agent framework choosing between an OpenAI and an Ollama backend at runtime) has to maintain a translation table. This PR puts that table inside Prism.What changed
Prism\Prism\Concerns\HasReasoningtrait with a?boolstate andwithReasoning()/reasoningEnabled()accessors. Composed ontoText\PendingRequest,Text\Request,Structured\PendingRequest,Structured\Request.$request->reasoningEnabled()and emits the right key when it isfalseand the user has not already set the equivalent provider option:withReasoning(false)think: falsereasoning: { effort: "minimal" }thinkingblock omittedthinkingConfig: { thinkingBudget: 0 }reasoning: { exclude: true }reasoning_effort: "low"Other providers (Groq, Mistral, DeepSeek, xAI, etc.) treat the call as a graceful no-op — no error, no extra key sent.
Example
Backward compatibility
Fully additive. If
withReasoning()is never called the request behaves exactly as it does today. If a caller sets the equivalent provider option explicitly (e.g.withProviderOptions(['reasoning' => ['effort' => 'high']])), that explicit value still wins —withReasoning(false)only fills in the default when no provider option is set.Tests
withReasoning(false)intests/Providers/Ollama/TextTest.php,tests/Providers/Ollama/StreamTest.php,tests/Providers/OpenAI/TextTest.php,tests/Providers/Anthropic/AnthropicTextRequestTest.php. Each covers (a) the new wire-format key is sent, (b) explicit provider options still win, (c) absence of the key whenwithReasoning()is not called.vendor/bin/pest --parallelreports 1471 passing / 8 pre-existing skipped.vendor/bin/pint --testandvendor/bin/phpstan analyseboth clean.Naming
Followed existing conventions:
withMaxSteps,withMaxTokens,withProviderOptionsalready use awith*prefix onPendingRequest.withReasoning(bool $enabled = true)mirrors that shape and lets the common case read naturally as->withReasoning(false).