Skip to content

Commit 43b2d74

Browse files
author
Robin Picard
committed
Create the Macro class
1 parent f4497dd commit 43b2d74

File tree

6 files changed

+168
-0
lines changed

6 files changed

+168
-0
lines changed

docs/api/macros.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: outlines.macros

docs/reference/macros.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Macros
2+
3+
Outlines offers a convenient way of encapsulating a model, a prompt template, and an output type in a single object called a `Macro`. After instantiating a `Macro`, it can be called just like a function with arguments that will be passed to the template to create the prompt. The prompt is then used to call the model with the output type first specified to generate an answer.
4+
5+
## Create a Macro
6+
7+
To create a Macro, you need to provide 3 arguments:
8+
9+
- A model: an instance of an Outlines model class from module `outlines.models`
10+
- A template: either an instance of `outlines.templates.Template` or a callable that takes arguments and returns a prompt
11+
- An output type: a type instance from `outlines.types` that is used to define the structure of the output
12+
13+
```python
14+
from pydantic import BaseModel
15+
from outlines.models import transformers
16+
from outlines.templates import Template
17+
from outlines.types import JsonType
18+
19+
class OutputModel(BaseModel):
20+
result: int
21+
22+
model = transformers.from_transformers(
23+
"microsoft/Phi-3-mini-4k-instruct",
24+
"microsoft/Phi-3-mini-4k-instruct"
25+
)
26+
template = Template.from_str("What is 2 times {{ num }}?")
27+
output_type = JsonType(OutputModel)
28+
29+
macro = Macro(model, template, output_type)
30+
```
31+
32+
## Call a Macro
33+
34+
Once the Macro is instantiated, it can be called just like a function with arguments that will be passed to the template to create the prompt. The prompt is then used to call the model with the output type first specified to generate an answer.
35+
36+
```python
37+
result = macro(num=3)
38+
print(result) # Expected output: { "result" : 6 }
39+
```

outlines/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from outlines.base import vectorize
1010
from outlines.caching import clear_cache, disable_cache, get_cache
1111
from outlines.function import Function
12+
from outlines.macros import Macro
1213
from outlines.generate import Generator
1314
from outlines.templates import Template, prompt
1415

outlines/function.py

+12
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,25 @@ class Function:
2020
the function can be called with arguments that will be used to render the
2121
prompt template.
2222
23+
Note:
24+
This class is part of the deprecated 'function' module and is deprecated
25+
starting from version 1.0.0. It will be removed in version 1.1.0. Please
26+
pin your version to <1.1.0 if you need to continue using it.
27+
2328
"""
2429

2530
prompt_template: "Template"
2631
schema: Union[str, Callable, object]
2732
model_name: str
2833
generator: Optional["SequenceGeneratorAdapter"] = None
2934

35+
def __post_init__(self):
36+
raise DeprecationWarning(
37+
"The 'function' module is deprecated starting from version 1.0.0 "
38+
+ "and will be removed in version 1.1.0. Please use the `Macro` "
39+
+ "class instead. See https://github.com/dottxt-ai/outlines/tree/main/docs/reference/macros.md"
40+
)
41+
3042
@classmethod
3143
def from_github(cls, program_path: str, function_name: str = "fn"):
3244
"""Load a function stored on GitHub"""

outlines/macros.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from dataclasses import dataclass
2+
from typing import Any, Callable, Union
3+
4+
from outlines.generate import Generator
5+
from outlines.templates import Template
6+
from outlines.models import Model
7+
8+
9+
@dataclass
10+
class Macro:
11+
"""
12+
Macro is a class that encapsulates a model, a prompt template, and an
13+
output type. It can be called to generate a response.
14+
15+
Parameters
16+
----------
17+
model : Model
18+
The Outlines model to be used for generating responses.
19+
template : Union[Template, Callable]
20+
A callable that takes arguments and returns a prompt string.
21+
output_type : Any
22+
The expected output type of the generated response.
23+
24+
Examples
25+
--------
26+
from pydantic import BaseModel
27+
from transformers import AutoModelForCausalLM, AutoTokenizer
28+
from outlines import models, Macro
29+
from outlines.types import JsonType
30+
from outlines.templates import Template
31+
32+
class OutputModel(BaseModel):
33+
result: int
34+
35+
model = models.from_transformers(
36+
AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct"),
37+
AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct")
38+
)
39+
40+
template_string = "What is 2 times {{ num }}?"
41+
template = Template.from_str(template_string)
42+
43+
my_macro = Macro(model, template, JsonType(OutputModel))
44+
45+
result = my_macro(num=3)
46+
print(result) # Expected output: { "result" : 6 }
47+
"""
48+
model: Model
49+
template: Union[Template, Callable]
50+
output_type: Any
51+
52+
def __post_init__(self):
53+
self.template = self.template
54+
self.generator = Generator(self.model, self.output_type)
55+
56+
def __call__(self, *args, **kwargs):
57+
prompt = self.template(*args, **kwargs)
58+
return self.generator(prompt)

tests/test_macros.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import pytest
2+
3+
import jinja2
4+
5+
from outlines.macros import Macro
6+
from outlines.templates import Template
7+
from outlines.models import Model
8+
from outlines.generate import Generator
9+
from typing import Any
10+
11+
12+
@pytest.fixture(scope="session")
13+
def model():
14+
class MockModel(Model):
15+
type_adapter = None
16+
17+
def generate(self, model_input: str, output_type=None, **kwargs) -> Any:
18+
return model_input
19+
20+
return MockModel()
21+
22+
23+
def test_macro_initialization(model):
24+
template = Template.from_str("Test {{ value }}")
25+
output_type = None
26+
macro = Macro(model, template, output_type)
27+
28+
assert macro.generator == Generator(model, output_type)
29+
assert macro.template == template
30+
31+
32+
def test_macro_template_call(model):
33+
template = Template.from_str("Test {{ value }}")
34+
output_type = None
35+
macro = Macro(model, template, output_type)
36+
result = macro(value="example")
37+
38+
assert result == "Test example"
39+
40+
41+
def test_macro_callable_call(model):
42+
def template(value):
43+
return f"Test {value}"
44+
45+
output_type = None
46+
macro = Macro(model, template, output_type)
47+
result = macro("example")
48+
49+
assert result == "Test example"
50+
51+
def test_macro_template_error(model):
52+
template = Template.from_str("Test {{ value }}")
53+
output_type = None
54+
macro = Macro(model, template, output_type)
55+
56+
with pytest.raises(jinja2.exceptions.UndefinedError):
57+
macro(foo="bar")

0 commit comments

Comments
 (0)