Strictly Generate JSON
Author: Ryul0rd
Pull request: no

### Feature request

It would be nice if we could force a generative LM to only produce JSON with a specific schema. I was thinking the end user should be able to do something as simple as `model.generate_json(input_ids, tokenizer, schema=MyDataclass)`. More specifically, `MyDataclass` should be any dataclass made up of the following types: int, float, bool, str, list, option, enum (treated as strings in the JSON), or another dataclass following these rules.

I've already gone ahead and done a proof of concept showing this is possible [here](https://github.com/Ryul0rd/llm-json-output). I haven't put it in a PR because the code is a mess and it has very poor performance (10x or more normal inference time) but it otherwise works. The performance aspect in particular is an issue because I did some profiling and there doesn't seem to be anything I'm doing that's obviously inefficient so this might just be a limitation of Python and using a language like C++ or Rust might be necessary. I'm not sure how the maintainers feel about adding either of these languages to the library.

Another feature my implementation doesn't currently support is adding additional constraints beyond types. eg. min or max length on strings or arrays, min or max values on ints and floats. The max length on strings and arrays is particularly important because without that you can't guarantee the full JSON string will fit in your output token budget.

### Motivation

People are starting to build LLMs into larger apps by creating plugins/chains/agents etc. In many of these cases, we want the LM to produce some structured output of some kind that we can then parse. JSON is a common choice here but simply prompting a model and hoping for the best doesn't always work exactly the way you want it to. The black box nature of ANNs means failures are hard to predict and debug. Forcing the model to output valid JSON with a certain schema would improve the ability of developers to reason about the space of the model output.

This also has the rather nice property that you can get reasonable output from models that aren't instruct/assistant finetuned. Check out this example from GPT2. The first 3 lines are the prompt and the fourth is generated:
```
Plain Text:
Max is a 37 year old guy. His hobbies include gaming and martial arts.
JSON:
{"name":"Max","age":37,"is_male":true,"email_address":null}
```
Without forcing it to adhere to the JSON as output, GPT2 produces the following:
```
Plain Text:
Max is a 37 year old guy. His hobbies include gaming and martial arts.
JSON:
Max is a 37 year old guy. His hobbies include gaming and martial arts.
JSON:
Max is a 37 year old guy. His hobbies include gaming and martial arts.
JSON:
Max is a 37 year old guy. His hobbies include gaming and martial arts.
JSON:
Max is a 37
```

### Your contribution

I'd be open to writing the final code and making a PR once I get people's thoughts/advice depending on what people think of the performance/language issue. I've been learning Rust recently but am not an expert.

Comments:
Hi,

This seems very similar to this repo: https://github.com/1rgs/jsonformer. It's a wrapper around HF Transformer models (specifically, `xxxForCausalLM` models) to only fill in the values, not the keys of JSON schemas.

Thanks for pointing out that repo to me as I was unaware. It does claim to do exactly what I want but does have some issues currently, including both a failure to actually generate correct JSON reliably (The example I used above made it throw an error) and performance issues similar to my own. I took a look at how they were approaching the problem and I don't think they can fix their performance issues without a complete rewrite either.

I think I'll take a stab at the Rust rewrite and see how it goes. Would having Rust in transformers be an issue? If so I can just make my own library but I do think it would be better if the feature had the visibility boost of actually being in transformers.

Since the fast core of the tokenizers library is also implemented in Rust it shouldn't be an issue to have your implementation in Rust as well.

Btw: Did you take a look at [Kor](https://github.com/eyurtsev/)? It tries a similar thing within langchain...

I did look at Kor. My issue with it is that it's just using the "prompt and hope for the best" approach rather than actually providing any sort of guarantees about the output like jsonformer and my approach are doing.

Yes it's more like a prompt template engine. One cool feature is that they support pydantic models.

@Ryul0rd author of https://github.com/1rgs/jsonformer here, ended up fixing a few bugs and perf issues over the last day. Can you try once again? If it doesn't work can you send me a repro case? Thanks!

Example notebook here: https://colab.research.google.com/github/1rgs/jsonformer/blob/main/Jsonformer_example.ipynb

There are now several libraries that support various methods of getting structured output from LMs. In addition to jsonformer, there's also [guidance](https://github.com/microsoft/guidance) and [LMQL](https://lmql.ai/). I'd say all 3 are still in a pretty rough/early stage at the moment but they've all got slightly different ideas about how to do things so. It still might be worth adding something like this to transformers at some point but it might be worth holding off a bit until we see which ideas work out best.

This issue has been automatically marked as stale because it has not had recent activity. If you think this still needs to be addressed please comment on this thread.

Please note that issues that do not follow the [contributing guidelines](https://github.com/huggingface/transformers/blob/main/CONTRIBUTING.md) are likely to be ignored.

any update on this

Another work which might be worth exploring: https://github.com/normal-computing/outlines.

Why was this closed? Has it been resolved?

It's closed because the issue was marked stale. I don't think this is a feature that would be natively integrated into the Transformers library, as external libraries like Outlines are compatible with Transformers.