Skip to content

Commit aed9d21

Browse files
committed
Improve the documentation for structured generation
1 parent d825d0c commit aed9d21

File tree

13 files changed

+258
-130
lines changed

13 files changed

+258
-130
lines changed

docs/api/guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: outlines.fsm.guide

docs/reference/cfg.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,63 @@
11
# Grammar-structured generation
2+
3+
You can pass any context-free grammar in the EBNF format and Outlines will generate an output that is valid to this grammar:
4+
5+
```python
6+
from outlines import models, generate
7+
8+
arithmetic_grammar = """
9+
?start: expression
10+
11+
?expression: term (("+" | "-") term)*
12+
13+
?term: factor (("*" | "/") factor)*
14+
15+
?factor: NUMBER
16+
| "-" factor
17+
| "(" expression ")"
18+
19+
%import common.NUMBER
20+
"""
21+
22+
model = models.transformers("WizardLM/WizardMath-7B-V1.1")
23+
generator = generate.cfg(model, arithmetic_grammar)
24+
sequence = generator(
25+
"Alice had 4 apples and Bob ate 2. "
26+
+ "Write an expression for Alice's apples:"
27+
)
28+
29+
print(sequence)
30+
# (8-2)
31+
```
32+
33+
!!! Note "Performance"
34+
35+
The implementation of grammar-structured generation in Outlines is very naive. This does not reflect the performance of [.txt](https://dottxt.co)'s product, where we made grammar-structured generation as fast as regex-structured generation.
36+
37+
38+
## Ready-to-use grammars
39+
40+
Outlines contains a (small) library of grammars that can be imported and use directly. We can rewrite the previous example as:
41+
42+
```python
43+
from outlines import models, generate
44+
45+
arithmetic_grammar = outlines.grammars.arithmetic
46+
47+
model = models.transformers("WizardLM/WizardMath-7B-V1.1")
48+
generator = generate.cfg(model, arithmetic_grammar)
49+
sequence = generator(
50+
"Alice had 4 apples and Bob ate 2. "
51+
+ "Write an expression for Alice's apples:"
52+
)
53+
54+
print(sequence)
55+
# (8-2)
56+
```
57+
58+
The following grammars are currently available:
59+
60+
- Arithmetic grammar via `outlines.grammars.arithmetic`
61+
- JSON grammar via `outlines.grammars.json`
62+
63+
If you would like more grammars to be added to the repository, please open an [issue](https://github.com/outlines-dev/outlines/issues) or a [pull request](https://github.com/outlines-dev/outlines/pulls).

docs/reference/choices.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
# Multiple choices
22

3-
Choice between different options
4-
In some cases we know the output is to be chosen between different options. We can restrict the completion’s output to these choices using the is_in keyword argument:
3+
Oultines allows you to make sure the generated text is chosen between different options:
54

65
```python
7-
import outlines.models as models
6+
from outlines import models, generate
7+
8+
model = models.transformers("mistralai/Mistral-7B-v0.1")
9+
generator = generate.choice(model, ["skirt", "dress", "pen", "jacket"])
10+
answer = generator("Pick the odd word out: skirt, dress, pen, jacket")
811

9-
complete = models.openai("gpt-3.5-turbo")
10-
answer = complete(
11-
"Pick the odd word out: skirt, dress, pen, jacket",
12-
is_in=["skirt", "dress", "pen", "jacket"]
13-
)
1412
```
13+
14+
!!! Note "Performance"
15+
16+
`generation.choice` computes an index that helps Outlines guide generation. This can take some time, but only needs to be done once. If you want to generate from the same list of choices several times make sure that you only call `generate.choice` once.

docs/reference/custom_fsm_ops.md

Lines changed: 14 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,37 @@
11
# Custom FSM Operations
22

3-
```RegexFSM.from_interegular_fsm``` leverages the flexibility of ```interegular.FSM``` to use the available operations in ```interegular```.
3+
Outlines is fast because it compiles regular expressions into an index ahead of inference. To do so we use the equivalence between regular expressions and Finite State Machines (FSMs), and the library [interegular](https://github.com/MegaIng/interegular) to perform the translation.
44

5-
## Examples
5+
Alternatively, one can pass a FSM built using `integular` directly to structure the generation.
66

7-
### ```difference```
7+
## Example
88

9-
Returns an FSM which recognises only the strings recognised by the first FSM in the list, but none of the others.
9+
### Using the `difference` operation
10+
11+
In the following example we build a fsm which recognizes only the strings valid to the first regular expression but not the second. In particular, it will prevent the words "pink" and "elephant" from being generated:
1012

1113
```python
14+
import interegular
15+
from outlines import models, generate
16+
17+
1218
list_of_strings_pattern = """\["[^"\s]*"(?:,"[^"\s]*")*\]"""
1319
pink_elephant_pattern = """.*(pink|elephant).*"""
1420

1521
list_of_strings_fsm = interegular.parse_pattern(list_of_strings_pattern).to_fsm()
1622
pink_elephant_fsm = interegular.parse_pattern(pink_elephant_pattern).to_fsm()
1723

18-
list_of_strings_fsm.accepts('["a","pink","elephant"]')
19-
# True
20-
2124
difference_fsm = list_of_strings_fsm - pink_elephant_fsm
2225

2326
difference_fsm_fsm.accepts('["a","pink","elephant"]')
2427
# False
2528
difference_fsm_fsm.accepts('["a","blue","donkey"]')
2629
# True
27-
```
28-
29-
### ```union```
30-
31-
Returns a finite state machine which accepts any sequence of symbols that is accepted by either self or other.
32-
33-
```python
34-
list_of_strings_pattern = """\["[^"\s]*"(?:,"[^"\s]*")*\]"""
35-
tuple_of_strings_pattern = """\("[^"\s]*"(?:,"[^"\s]*")*\)"""
36-
37-
list_of_strings_fsm = interegular.parse_pattern(list_of_strings_pattern).to_fsm()
38-
tuple_of_strings_fsm = interegular.parse_pattern(tuple_of_strings_pattern).to_fsm()
39-
40-
list_of_strings_fsm.accepts('("a","pink","elephant")')
41-
# False
4230

43-
union_fsm = list_of_strings_fsm|tuple_of_strings_fsm
4431

45-
union_fsm.accepts('["a","pink","elephant"]')
46-
# True
47-
union_fsm.accepts('("a","blue","donkey")')
48-
# True
32+
model = models.transformers("mistralai/Mistral-7B-Instruct-v0.2")
33+
generator = generate.fsm(model, difference_fsm)
34+
response = generator("Don't talk about pink elephants")
4935
```
5036

51-
### ```intersection```
52-
53-
Returns an FSM which accepts any sequence of symbols that is accepted by both of the original FSMs.
54-
55-
```python
56-
list_of_strings_pattern = """\["[^"\s]*"(?:,"[^"\s]*")*\]"""
57-
pink_elephant_pattern = """.*(pink|elephant).*"""
58-
59-
list_of_strings_fsm = interegular.parse_pattern(list_of_strings_pattern).to_fsm()
60-
pink_elephant_fsm = interegular.parse_pattern(pink_elephant_pattern).to_fsm()
61-
62-
list_of_strings_fsm.accepts('["a","blue","donkey"]')
63-
# True
64-
65-
intersection_fsm = list_of_strings_fsm & pink_elephant_fsm
66-
67-
intersection_fsm.accepts('["a","pink","elephant"]')
68-
# True
69-
intersection_fsm.accepts('["a","blue","donkey"]')
70-
# False
71-
```
72-
73-
_There are more operations available, we refer to https://github.com/MegaIng/interegular/blob/master/interegular/fsm.py._
74-
75-
# Loading Custom FSM
76-
77-
```python
78-
import outlines
79-
80-
generator = outlines.generate.fsm(model, custom_fsm)
81-
82-
response = generator(prompt)
83-
```
37+
To see the other operations available, consult [interegular's documentation](https://github.com/MegaIng/interegular/blob/master/interegular/fsm.py).

docs/reference/json.md

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Make the LLM follow a JSON Schema
1+
# JSON structured generation
22

33
Outlines can make any open source model return a JSON object that follows a structure that is specified by the user. This is useful whenever we want the output of the model to be processed by code downstream: code does not understand natural language but rather the structured language it has been programmed to understand.
44

@@ -16,8 +16,7 @@ Outlines can infer the structure of the output from a Pydantic model. The result
1616
```python
1717
from pydantic import BaseModel
1818

19-
from outlines import models
20-
from outlines import text
19+
from outlines import models, generate
2120

2221

2322
class User(BaseModel):
@@ -27,20 +26,59 @@ class User(BaseModel):
2726

2827

2928
model = models.transformers("mistralai/Mistral-7B-v0.1")
30-
generator = text.generate.json(model, User)
31-
result = generator("Create a user profile with the fields name, last_name and id")
29+
generator = generate.json(model, User)
30+
result = generator(
31+
"Create a user profile with the fields name, last_name and id"
32+
)
3233
print(result)
3334
# User(name="John", last_name="Doe", id=11)
3435
```
3536

36-
!!! warning "JSON and whitespaces"
37+
!!! Note "JSON and whitespaces"
3738

3839
By default Outlines lets model choose the number of linebreaks and white spaces used to structure the JSON. Small models tend to struggle with this, in which case we recommend to set the value of the parameter `whitespace_pattern` to the empty string:
3940

4041
```python
41-
generator = text.generate.json(model, User, whitespace_pattern="")
42+
generator = generate.json(model, User, whitespace_pattern="")
4243
```
4344

45+
!!! Note "Performance"
46+
47+
`generation.json` computes an index that helps Outlines guide generation. This can take some time, but only needs to be done once. If you want to generate several times with the same schema make sure that you only call `generate.json` once.
48+
49+
50+
## Using a JSON Schema
51+
52+
Instead of a Pydantic model you can pass a string that represents a [JSON Schema](https://json-schema.org/) specification to `generate.json`:
53+
54+
```python
55+
from pydantic import BaseModel
56+
57+
from outlines import models
58+
from outlines import text
59+
60+
model = models.transformers("mistralai/Mistral-7B-v0.1")
61+
62+
schema = """
63+
{
64+
"title": "User",
65+
"type": "object",
66+
"properties": {
67+
"name": {"type": "string"},
68+
"last_name": {"type": "string"},
69+
"id": {"type": "integer"}
70+
}
71+
}
72+
"""
73+
74+
generator = generate.json(model, schema)
75+
result = generator(
76+
"Create a user profile with the fields name, last_name and id"
77+
)
78+
print(result)
79+
# User(name="John", last_name="Doe", id=11)
80+
```
81+
4482
## From a function's signature
4583

4684
Outlines can infer the structure of the output from the signature of a function. The result is a dictionary, and can be passed directly to the function using the usual dictionary expansion syntax `**`:

docs/reference/json_mode.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# JSON mode
2+
3+
Outlines can guarantee that the LLM will generate valid JSON, using [Grammar-structured generation](cfg.md):
4+
5+
```python
6+
from outlines import models, generate
7+
8+
json_grammar = outlines.grammars.json
9+
10+
model = models.transformers("mistralai/Mistral-7b-v0.1")
11+
generator = generate.cfg(model, json_grammar)
12+
sequence = generator("Generate valid JSON")
13+
```
14+
15+
!!! Note "JSON that follows a schema"
16+
17+
If you want to guarantee that the generated JSON follows a given schema, consult [this section](json.md) instead.

docs/reference/regex.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,27 @@
11
# Regular expressions
2+
3+
Outlines can guarantee that the text generated by the LLM will be valid to a regular expression:
4+
5+
```python
6+
from outlines import models, generate
7+
8+
model = models.transformers("mistralai/Mistral-7B-Instruct-v0.2")
9+
10+
generator = generate.regex(
11+
model,
12+
r"((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)",
13+
)
14+
15+
prompt = "What is the IP address of the Google DNS servers? "
16+
answer = generator(prompt, max_tokens=30)
17+
18+
print(answer)
19+
# What is the IP address of the Google DNS servers?
20+
# 2.2.6.1
21+
```
22+
23+
If you find yourself using `generate.regex` to restrict the answers' type you can take a look at [type-structured generation](types.md) instead.
24+
25+
!!! Note "Performance"
26+
27+
`generate.regex` computes an index that helps Outlines guide generation. This can take some time, but only needs to be done once. If you want to generate several times using the same regular expression make sure that you only call `generate.regex` once.

docs/reference/samplers.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,8 @@ answer = generator("What is 2+2?")
108108
print(answer)
109109
# 4
110110
```
111+
112+
113+
!!! Warning "Compatibility"
114+
115+
Only models from the `transformers` and `exllamav2 ` libraries are compatible with Beam Search.

0 commit comments

Comments
 (0)