feat(ai): support constrained sampling#6341
Open
mitsuhiko wants to merge 8 commits into
Open
Conversation
Add the constrained sampling API and provider plumbing for OpenAI and Anthropic tool calls, including grammar sampling variants and model capability metadata.
mitsuhiko
commented
Jul 5, 2026
| chatTemplateKwargs: {}, | ||
| zaiToolStream: false, | ||
| supportsStrictMode: !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia, | ||
| supportsGrammarTools: provider === "openai", |
Member
Author
There was a problem hiding this comment.
Maybe this field should be called supportsOpenAIGrammarTools.
Collaborator
|
how do we gate this to openai as a provider? how does this work via proxies? |
Grammar tool capability is now model metadata instead of a runtime provider heuristic. The model generator stamps compat.supportsGrammarTools on GPT-5+ models for endpoints verified or documented to pass OpenAI custom tools through (OpenAI, OpenAI Codex, Azure OpenAI, GitHub Copilot, opencode, Cloudflare AI Gateway). All OpenAI-family APIs read only the compat flag and default to off, since gateways that normalize tool schemas (e.g. OpenRouter) mangle custom tools and OpenAI rejects them for pre-GPT-5 models. Azure and Codex Responses now support grammar tools and carry OpenAIResponsesCompat.
# Conflicts: # packages/ai/src/api/openai-responses-shared.ts
Member
Author
|
We enable grammar tools through per-model {
"providers": {
"my-proxy": {
"api": "openai-responses",
"baseUrl": "https://proxy.example/v1",
"compat": {
"supportsGrammarTools": true
},
"models": [{ "id": "gpt-5.4" }]
}
}
} |
This was referenced Jul 10, 2026
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.
This adds an opt-in
constrainedSamplingconfig for tools so callers can request provider-side constrained tool input generation.Basically tools can now ask the model provider to constrain tool arguments in two ways:
stricttool calling:strict: "prefer"uses strict provider behavior when available and falls back otherwise.strict: "require"fails clearly if the selected provider/model cannot honor it.Now there are some notes here that this PR does not address today. One of the motivations for
preferovertrue/falseis that if you define too many tools to be strict, inference APIs can fail. The motivation here is that we can later (not done today) be more clever in how we deal with strictness. For now that's just over-engineering.The second note is that only OpenAI today supports constraining for tool calls with custom grammar and it's unclear what other APIs will do in the future. Quite a few inference APIs support constraining on some grammar for regular responses for structured completions but that's not really applicable for pi today.
Because I do not want to design some feature around some OpenAI oddity the change here detains grammar-constrained tools to be like ordinary pi tools to callers: for custom grammar tools we use a normal object schema with one required string property. The idea here is that for providers without native grammar support we have a clear way to fall back to the normal JSON/function tool form. Custom providers and model configs can also declare whether they support strict tools or grammar tools, so pi does not assume that every OpenAI-compatible or Anthropic-compatible endpoint accepts these newer fields.
This all came out of the edit-tool investigation in #6278. Newer Claude models can sometimes produce semantically correct nested tool arguments with extra invented fields, especially in long agentic histories. To support
apply_patchand other things from OpenAI the entire approach really only makes sense if you consider it as a free form tool so that's where the desire came in to support these grammars.My experiments show that strict/constrained decoding can eliminate that class of failure and I want to put us into a position where we or users can use OpenAI specific custom tools as well.
This is what this looks like to the user for just constraining on JSON schema:
And this is what it looks like with custom grammar for OpenAI only:
For the grammar case, pi still exposes
{ input: string }as the tool arguments. I also set up the grammar based sampling so that in case some other tool comes around in the future, you can define more than one grammar.Fixes #6306