Skip to content

Commit

Permalink
Create the Macro class
Browse files Browse the repository at this point in the history
  • Loading branch information
Robin Picard committed Feb 27, 2025
1 parent f4497dd commit 43b2d74
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/api/macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: outlines.macros
39 changes: 39 additions & 0 deletions docs/reference/macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Macros

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.

## Create a Macro

To create a Macro, you need to provide 3 arguments:

- A model: an instance of an Outlines model class from module `outlines.models`
- A template: either an instance of `outlines.templates.Template` or a callable that takes arguments and returns a prompt
- An output type: a type instance from `outlines.types` that is used to define the structure of the output

```python
from pydantic import BaseModel
from outlines.models import transformers
from outlines.templates import Template
from outlines.types import JsonType

class OutputModel(BaseModel):
result: int

model = transformers.from_transformers(
"microsoft/Phi-3-mini-4k-instruct",
"microsoft/Phi-3-mini-4k-instruct"
)
template = Template.from_str("What is 2 times {{ num }}?")
output_type = JsonType(OutputModel)

macro = Macro(model, template, output_type)
```

## Call a Macro

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.

```python
result = macro(num=3)
print(result) # Expected output: { "result" : 6 }
```
1 change: 1 addition & 0 deletions outlines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from outlines.base import vectorize
from outlines.caching import clear_cache, disable_cache, get_cache
from outlines.function import Function
from outlines.macros import Macro
from outlines.generate import Generator
from outlines.templates import Template, prompt

Expand Down
12 changes: 12 additions & 0 deletions outlines/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,25 @@ class Function:
the function can be called with arguments that will be used to render the
prompt template.
Note:
This class is part of the deprecated 'function' module and is deprecated
starting from version 1.0.0. It will be removed in version 1.1.0. Please
pin your version to <1.1.0 if you need to continue using it.
"""

prompt_template: "Template"
schema: Union[str, Callable, object]
model_name: str
generator: Optional["SequenceGeneratorAdapter"] = None

def __post_init__(self):
raise DeprecationWarning(
"The 'function' module is deprecated starting from version 1.0.0 "
+ "and will be removed in version 1.1.0. Please use the `Macro` "
+ "class instead. See https://github.com/dottxt-ai/outlines/tree/main/docs/reference/macros.md"
)

@classmethod
def from_github(cls, program_path: str, function_name: str = "fn"):
"""Load a function stored on GitHub"""
Expand Down
58 changes: 58 additions & 0 deletions outlines/macros.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from dataclasses import dataclass
from typing import Any, Callable, Union

from outlines.generate import Generator
from outlines.templates import Template
from outlines.models import Model


@dataclass
class Macro:
"""
Macro is a class that encapsulates a model, a prompt template, and an
output type. It can be called to generate a response.
Parameters
----------
model : Model
The Outlines model to be used for generating responses.
template : Union[Template, Callable]
A callable that takes arguments and returns a prompt string.
output_type : Any
The expected output type of the generated response.
Examples
--------
from pydantic import BaseModel
from transformers import AutoModelForCausalLM, AutoTokenizer
from outlines import models, Macro
from outlines.types import JsonType
from outlines.templates import Template
class OutputModel(BaseModel):
result: int
model = models.from_transformers(
AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct"),
AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct")
)
template_string = "What is 2 times {{ num }}?"
template = Template.from_str(template_string)
my_macro = Macro(model, template, JsonType(OutputModel))
result = my_macro(num=3)
print(result) # Expected output: { "result" : 6 }
"""
model: Model
template: Union[Template, Callable]
output_type: Any

def __post_init__(self):
self.template = self.template
self.generator = Generator(self.model, self.output_type)

def __call__(self, *args, **kwargs):
prompt = self.template(*args, **kwargs)
return self.generator(prompt)
57 changes: 57 additions & 0 deletions tests/test_macros.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import pytest

import jinja2

from outlines.macros import Macro
from outlines.templates import Template
from outlines.models import Model
from outlines.generate import Generator
from typing import Any


@pytest.fixture(scope="session")
def model():
class MockModel(Model):
type_adapter = None

def generate(self, model_input: str, output_type=None, **kwargs) -> Any:
return model_input

return MockModel()


def test_macro_initialization(model):
template = Template.from_str("Test {{ value }}")
output_type = None
macro = Macro(model, template, output_type)

assert macro.generator == Generator(model, output_type)
assert macro.template == template


def test_macro_template_call(model):
template = Template.from_str("Test {{ value }}")
output_type = None
macro = Macro(model, template, output_type)
result = macro(value="example")

assert result == "Test example"


def test_macro_callable_call(model):
def template(value):
return f"Test {value}"

output_type = None
macro = Macro(model, template, output_type)
result = macro("example")

assert result == "Test example"

def test_macro_template_error(model):
template = Template.from_str("Test {{ value }}")
output_type = None
macro = Macro(model, template, output_type)

with pytest.raises(jinja2.exceptions.UndefinedError):
macro(foo="bar")

0 comments on commit 43b2d74

Please sign in to comment.