Constrained generation

You can also use the Constrainer class to just generate text based on constraints. You then have two options: either using a builtin JSON schema constraint or a custom one.

JSON schema

You can generate based on a simple JSON schema. Note that this does not support the full jsonschema specification, but instead uses a simplified format similar to that of OpenAI. Here’s a simple usage example:

from local_llm_function_calling import Constrainer, JsonSchemaConstraint
from local_llm_function_calling.model.huggingface import HuggingfaceModel


schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string", "maxLength": 10},
        "age": {"type": "integer"}
    },
    "enforceOrder": ["name", "age"]
}

constraint = JsonSchemaConstraint(schema)
constrainer = Constrainer(HuggingfaceModel("gpt2"))
raw_json = constrainer.generate("Prefix.\n", constraint, max_len=100)
truncated_json = raw_json[:constraint.validate(raw_json).end_index]
This is the generated JSON:
{
  "name": "TheTheThe.",
  "age": -1
}

Note that gpt2 was used, so it’s irrational to expect high quality output.

raw_json can containe extra characters at the end, that’s why we then create truncated_json. The prefix will be prepended to the generated data and then used as the prompt for the model.

Custom constraints

If you don’t want the output to just adhere to a JSON schema, you can also define your own constraints. A constraint is just a callable that takes in the generated text and checks whether what’s been generated is valid and whether it’s the complete output. Here’s a simple example that forces the output to be all-lowercase.

def lowercase_sentence_constraint(text: str):
    # Has to return (is_valid, is_complete)
    return [text.islower(), text.endswith(".")]

constrainer = Constrainer(HuggingfaceModel("gpt2"))

generated = constrainer.generate("Prefix.\n", lowercase_sentence_constraint, max_len=10)