Skip to content

Add Mistral guidance#202

Merged
juliendenize merged 21 commits intomainfrom
add_grammar
Mar 31, 2026
Merged

Add Mistral guidance#202
juliendenize merged 21 commits intomainfrom
add_grammar

Conversation

@juliendenize
Copy link
Copy Markdown
Contributor

@juliendenize juliendenize commented Mar 20, 2026

This PR adds Mistral guidance with the following restrictions:

  • Tekken
  • version >= v11

So right now in the current state of the common PR what we do is:

  • auto strict=False => grammar enforce that optional content is before optional tool calls with a name (not enforced) and some json args.
  • auto strict=True => grammar enforce that optional content is before optional tool calls with a valid name and valid json args.
  • required strict=False => grammar enforce that optional content is before mandatory tool calls with a name (not enforced) and some json args.
  • required strict=True => grammar enforce that optional content is before mandatory tool calls with a valid name and valid json args.
  • `none grammar enforce mandatory content without tool calls
  • named strict=False => grammar enforce that optional content is before optional tool calls with the correct name and some json args.
  • named strict=True => grammar enforce that optional content is before optional tool calls with the correct name and valid json args.

If we add reasoning then it it the same but thinking trace is forced for tokenizer < v11 and optional above

@juliendenize juliendenize marked this pull request as ready for review March 25, 2026 15:04
r"""Returns a lark grammar that only accepts JSON objects matching the given schema."""
return f"start: SAFE_WS? %json {json.dumps(json_schema, ensure_ascii=False)} \nSAFE_WS: /[ \t\r\n]+/"

def get_matcher(self, lark: str) -> "llg.LLMatcher":
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.

why do we have so many functions in this grammar factory? I would have imagined to just have a single "get_grammar" function here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So get_matcher i could remove.

Otherwise, I tried to match internally implem. Can be changed if needed.

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.

Don't really see the point of having this method - can we remove?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes think it can be added later if we need it but agree that for now it doesn't bring any value.



class MistralLLGTokenizer:
r"""Wraps a Tekken tokenizer for use with llguidance.
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.

is this better than abstracting Tokenizer here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it doesn't have the same scope, here this tokenizer is just meant to be fed to guidance. I could even had a preleading _ to the class name to mark it private tbh

Copy link
Copy Markdown

@bbrowning bbrowning left a comment

Choose a reason for hiding this comment

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

Because I have a bit of experience writing Lark grammars for other models, I took a pass at reviewing this one. I'm not an expert in these models output formats, so please feel free to disregard or ignore these comments if they aren't helpful.

Comment on lines +85 to +87
if args == {}:
args = {"type": "object", "properties": {}, "additionalProperties": False}
return args
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.

Suggested change
if args == {}:
args = {"type": "object", "properties": {}, "additionalProperties": False}
return args
return args or {"type": "object", "properties": {}, "additionalProperties": False}

for tool in tools:
args = _get_tool_args_json(tool)
grammar_per_tool.append(
f'({tool_calls_token} SAFE_WS? "{tool.function.name}" {args_token} SAFE_WS? %json '
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.

let's not dup the same string here again

f"{len(self._special_token_ids)}"
)

def __call__(self, s: str, *args: Any, **kwargs: Any) -> list[int]:
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.

you should never be allowed to call bos=True or eos=True here?

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.

Should we throw a warning/error if *args / **kwargs includes anything? Not sure it's good to just silently swallow the input here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I replicated what we had internally but they aren't use so i just remove them.

)
assert '"additionalProperties": false' in result
assert '"properties": {}' in result
assert not result.endswith(")+")
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.

very nice tests!

Co-authored-by: Julien Denize <40604584+juliendenize@users.noreply.github.com>
@juliendenize juliendenize merged commit b0e133e into main Mar 31, 2026
6 checks passed
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.

4 participants