Skip to content

Python API Reference

Complete API reference for the Grammar School Python implementation.

Core Types

Value dataclass

Value(kind: str, value: Any)

A value in the AST (number, string, identifier, etc.).

Attributes

kind instance-attribute

kind: str

value instance-attribute

value: Any

Functions

Arg dataclass

Arg(name: str, value: Value | Expression | PropertyAccess)

A named argument to a call.

Attributes

name instance-attribute

name: str

value instance-attribute

value: Value | Expression | PropertyAccess

Functions

Call dataclass

Call(name: str, args: dict[str, Value | Expression | PropertyAccess])

A single function call with named arguments.

Attributes

args instance-attribute

args: dict[str, Value | Expression | PropertyAccess]

name instance-attribute

name: str

Functions

CallChain dataclass

CallChain(calls: list[Call] | Iterator[Call] | Iterable[Call] | None = None)

A chain of calls connected by dots (method chaining).

Can be initialized with a list, iterator, or any iterable of Call objects.

Initialize CallChain with calls.

Parameters:

Name Type Description Default
calls list[Call] | Iterator[Call] | Iterable[Call] | None

List, iterator, or iterable of Call objects. If None, creates empty chain.

None
Source code in grammar-school-python/grammar_school/ast.py
def __init__(self, calls: list[Call] | Iterator[Call] | Iterable[Call] | None = None):
    """
    Initialize CallChain with calls.

    Args:
        calls: List, iterator, or iterable of Call objects. If None, creates empty chain.
    """
    if calls is None:
        object.__setattr__(self, "calls", [])
    elif isinstance(calls, list):
        object.__setattr__(self, "calls", calls)
    else:
        # Convert iterator/iterable to list
        object.__setattr__(self, "calls", list(calls))

Attributes

calls class-attribute instance-attribute

calls: list[Call] = field(default_factory=list)

Functions

__iter__

__iter__() -> Iterator[Call]

Make CallChain iterable.

Source code in grammar-school-python/grammar_school/ast.py
def __iter__(self) -> Iterator[Call]:
    """Make CallChain iterable."""
    return iter(self.calls)

Method Decorators

method

method(func: Callable) -> Callable

Decorator to mark a method as a direct implementation handler.

Methods decorated with @method contain the actual implementation. The framework handles the Grammar/Runtime split internally.

Example

@method def greet(self, name): print(f"Hello, {name}!") # Can do anything here - side effects, state changes, etc.

Source code in grammar-school-python/grammar_school/grammar.py
def method(func: Callable) -> Callable:
    """
    Decorator to mark a method as a direct implementation handler.

    Methods decorated with @method contain the actual implementation.
    The framework handles the Grammar/Runtime split internally.

    Example:
        @method
        def greet(self, name):
            print(f"Hello, {name}!")
            # Can do anything here - side effects, state changes, etc.
    """
    func._is_method = True  # type: ignore[attr-defined]
    return func

Note: The Action and Runtime types still exist internally for the two-layer architecture, but users don't need to interact with them directly when using the unified @method interface.

Grammar

Grammar

Grammar(grammar: str | GrammarBuilder | dict[str, Any] | Path | None = None, grammar_file: str | Path | None = None)

Main Grammar class for Grammar School.

Subclass this and define @verb methods to create your DSL handlers. Then use parse(), compile(), or execute() to process DSL scripts.

The Two-Layer Architecture:

  1. @verb methods (in Grammar subclass):
  2. Transform DSL syntax into Action data structures
  3. Pure functions - no side effects, just return Actions
  4. Example: track(name="Drums") → Action(kind="create_track", payload={...})

  5. Runtime (separate class):

  6. Takes Actions and performs actual side effects
  7. Handles state management, I/O, database operations, etc.
  8. Example: Receives Action(kind="create_track", ...) → creates actual track in system

This separation allows: - Same Grammar to work with different Runtimes (testing vs production) - @verb methods to be testable without side effects - Runtime to manage state independently of Grammar logic

Example
from grammar_school import Grammar, verb, Action

class MyGrammar(Grammar):
    @verb
    def greet(self, name, _context=None):
        # Pure function - just returns Action, no side effects
        return Action(kind="greet", payload={"name": name})

# Default runtime prints actions - no need to import Runtime!
grammar = MyGrammar()
grammar.execute('greet(name="World")')

# Or provide a custom runtime for actual behavior
from grammar_school import Runtime

class MyRuntime(Runtime):
    def __init__(self):
        self.greetings = []  # Runtime manages state

    def execute(self, action: Action) -> None:
        # This is where side effects happen
        if action.kind == "greet":
            name = action.payload["name"]
            self.greetings.append(name)
            print(f"Hello, {name}!")

grammar = MyGrammar(runtime=MyRuntime())
grammar.execute('greet(name="World")')
    Initialize grammar with optional custom grammar definition.

    Args:
        grammar: Optional custom grammar. Can be:
                - String (Lark grammar definition)
                - GrammarBuilder instance
                - Dict (grammar config - will be loaded via load_grammar_from_config)
                - Path (to YAML/TOML grammar config file)
                - None (uses Grammar School's default)
        grammar_file: Optional path to YAML/TOML grammar config file (alternative to grammar)

    Example:
        ```python
        # Using @method handlers - simple and direct
        class MyDSL(Grammar):
            @method
            def greet(self, name):
                print(f"Hello, {name}!")

        dsl = MyDSL()  # No runtime needed
        dsl.execute('greet(name="World")')

        # Using string
        grammar = MyGrammar(grammar="start: call_chain

call_chain: call (DOT call)*")

        # Using GrammarBuilder
        from grammar_school import GrammarBuilder
        builder = GrammarBuilder.default()
        grammar = MyGrammar(grammar=builder)

        # Using config dict
        config = {
            "start": "start",
            "rules": [
                {"name": "start", "definition": "call_chain"},
                {"name": "call_chain", "definition": "call (DOT call)*"}
            ]
        }
        grammar = MyGrammar(grammar=config)

        # Using config file
        grammar = MyGrammar(grammar_file="grammar.yaml")
        # or
        grammar = MyGrammar(grammar="grammar.toml")  # Path as string
        ```
Source code in grammar-school-python/grammar_school/grammar.py
def __init__(
    self,
    grammar: str | GrammarBuilder | dict[str, Any] | Path | None = None,
    grammar_file: str | Path | None = None,
):
    """
    Initialize grammar with optional custom grammar definition.

    Args:
        grammar: Optional custom grammar. Can be:
                - String (Lark grammar definition)
                - GrammarBuilder instance
                - Dict (grammar config - will be loaded via load_grammar_from_config)
                - Path (to YAML/TOML grammar config file)
                - None (uses Grammar School's default)
        grammar_file: Optional path to YAML/TOML grammar config file (alternative to grammar)

    Example:
        ```python
        # Using @method handlers - simple and direct
        class MyDSL(Grammar):
            @method
            def greet(self, name):
                print(f"Hello, {name}!")

        dsl = MyDSL()  # No runtime needed
        dsl.execute('greet(name="World")')

        # Using string
        grammar = MyGrammar(grammar="start: call_chain\ncall_chain: call (DOT call)*")

        # Using GrammarBuilder
        from grammar_school import GrammarBuilder
        builder = GrammarBuilder.default()
        grammar = MyGrammar(grammar=builder)

        # Using config dict
        config = {
            "start": "start",
            "rules": [
                {"name": "start", "definition": "call_chain"},
                {"name": "call_chain", "definition": "call (DOT call)*"}
            ]
        }
        grammar = MyGrammar(grammar=config)

        # Using config file
        grammar = MyGrammar(grammar_file="grammar.yaml")
        # or
        grammar = MyGrammar(grammar="grammar.toml")  # Path as string
        ```
    """

    # Handle grammar parameter
    if grammar is None:
        if grammar_file is not None:
            # Load from file
            grammar_str = self._load_grammar_from_file(grammar_file)
        else:
            # Use default
            grammar_str = DEFAULT_GRAMMAR
    elif isinstance(grammar, dict):
        # Config dict - load it
        grammar_str = load_grammar_from_config(grammar)
    elif isinstance(grammar, str | Path) and (
        str(grammar).endswith(".yaml")
        or str(grammar).endswith(".yml")
        or str(grammar).endswith(".toml")
    ):
        # Path to config file
        grammar_str = self._load_grammar_from_file(grammar)
    elif isinstance(grammar, GrammarBuilder):
        # GrammarBuilder - convert to string
        grammar_str = grammar.build()
    else:
        # String (Lark grammar definition)
        grammar_str = str(grammar)

    self.backend = LarkBackend(grammar_str)
    self.interpreter = Interpreter(self)

Attributes

backend instance-attribute

backend = LarkBackend(grammar_str)

interpreter instance-attribute

interpreter = Interpreter(self)

Functions

compile

compile(code: str) -> list[None]

Compile DSL code by executing methods.

Note: Methods execute directly during compilation. Returns a list of None values (one per method call).

Source code in grammar-school-python/grammar_school/grammar.py
def compile(self, code: str) -> list[None]:
    """
    Compile DSL code by executing methods.

    Note: Methods execute directly during compilation.
    Returns a list of None values (one per method call).
    """
    call_chain = self.parse(code)
    return self.interpreter.interpret(call_chain)

execute

execute(code: str) -> None

Execute DSL code by calling methods directly.

Methods decorated with @method are executed immediately when called. No runtime is needed - methods contain their own implementation.

Parameters:

Name Type Description Default
code str

DSL code string to execute

required
Example
class MyDSL(Grammar):
    @method
    def greet(self, name):
        print(f"Hello, {name}!")

dsl = MyDSL()
dsl.execute('greet(name="World")')  # Prints: Hello, World!
Source code in grammar-school-python/grammar_school/grammar.py
def execute(self, code: str) -> None:
    """
    Execute DSL code by calling methods directly.

    Methods decorated with @method are executed immediately when called.
    No runtime is needed - methods contain their own implementation.

    Args:
        code: DSL code string to execute

    Example:
        ```python
        class MyDSL(Grammar):
            @method
            def greet(self, name):
                print(f"Hello, {name}!")

        dsl = MyDSL()
        dsl.execute('greet(name="World")')  # Prints: Hello, World!
        ```
    """
    call_chain = self.parse(code)
    # Execute methods directly - they run during interpretation
    for _ in self.interpreter.interpret_stream(call_chain):
        pass  # Methods execute during interpretation

parse

parse(code: str) -> CallChain

Parse DSL code into a CallChain AST.

Source code in grammar-school-python/grammar_school/grammar.py
def parse(self, code: str) -> CallChain:
    """Parse DSL code into a CallChain AST."""
    return self.backend.parse(code)

stream

stream(code: str)

Stream method executions from DSL code.

This is a generator that executes methods one at a time, allowing for memory-efficient processing and real-time execution of large DSL programs.

Parameters:

Name Type Description Default
code str

DSL code string to execute and stream

required

Yields:

Name Type Description
None

One None per method executed (methods execute during iteration)

Example
grammar = MyGrammar()
for _ in grammar.stream('greet(name="A").greet(name="B").greet(name="C")'):
    # Methods execute as they're called
    pass
Source code in grammar-school-python/grammar_school/grammar.py
def stream(self, code: str):
    """
    Stream method executions from DSL code.

    This is a generator that executes methods one at a time, allowing
    for memory-efficient processing and real-time execution of large DSL programs.

    Args:
        code: DSL code string to execute and stream

    Yields:
        None: One None per method executed (methods execute during iteration)

    Example:
        ```python
        grammar = MyGrammar()
        for _ in grammar.stream('greet(name="A").greet(name="B").greet(name="C")'):
            # Methods execute as they're called
            pass
        ```
    """
    call_chain = self.parse(code)
    yield from self.interpreter.interpret_stream(call_chain)

rule

rule(grammar: str | None = None, **kwargs: str | Any) -> Callable[[type[T]], type[T]]

Decorator to define grammar rules.

Supports three forms: 1. @rule("call_chain: call ('.' call)") 2. @rule(call_chain="call ('.' call)") 3. @rule(call_chain = sym("call") + many(lit(".") + sym("call")))

Source code in grammar-school-python/grammar_school/grammar.py
def rule(
    grammar: str | None = None,
    **kwargs: str | Any,
) -> Callable[[type[T]], type[T]]:
    """
    Decorator to define grammar rules.

    Supports three forms:
    1. @rule("call_chain: call ('.' call)*")
    2. @rule(call_chain="call ('.' call)*")
    3. @rule(call_chain = sym("call") + many(lit(".") + sym("call")))
    """
    if grammar is not None:

        def decorator_with_grammar(cls: type[T]) -> type[T]:
            if not hasattr(cls, "_grammar_rules"):
                cls._grammar_rules = {}  # type: ignore[attr-defined]
            cls._grammar_rules["_default"] = grammar  # type: ignore[attr-defined]
            return cls

        return decorator_with_grammar

    def decorator_with_kwargs(cls: type[T]) -> type[T]:
        if not hasattr(cls, "_grammar_rules"):
            cls._grammar_rules = {}  # type: ignore[attr-defined]
        for key, value in kwargs.items():
            cls._grammar_rules[key] = value  # type: ignore[attr-defined]
        return cls

    return decorator_with_kwargs

method

method(func: Callable) -> Callable

Decorator to mark a method as a direct implementation handler.

Methods decorated with @method contain the actual implementation. The framework handles the Grammar/Runtime split internally.

Example

@method def greet(self, name): print(f"Hello, {name}!") # Can do anything here - side effects, state changes, etc.

Source code in grammar-school-python/grammar_school/grammar.py
def method(func: Callable) -> Callable:
    """
    Decorator to mark a method as a direct implementation handler.

    Methods decorated with @method contain the actual implementation.
    The framework handles the Grammar/Runtime split internally.

    Example:
        @method
        def greet(self, name):
            print(f"Hello, {name}!")
            # Can do anything here - side effects, state changes, etc.
    """
    func._is_method = True  # type: ignore[attr-defined]
    return func

Interpreter

Interpreter

Interpreter(dsl_instance: Any)

Interprets CallChain AST and executes methods directly.

Initialize interpreter with a DSL instance containing method handlers.

Source code in grammar-school-python/grammar_school/interpreter.py
def __init__(self, dsl_instance: Any):
    """Initialize interpreter with a DSL instance containing method handlers."""
    self.dsl = dsl_instance
    self._method_handlers = self._collect_methods()

Attributes

dsl instance-attribute

dsl = dsl_instance

Functions

interpret

interpret(call_chain: CallChain) -> list[None]

Interpret a CallChain by executing methods directly.

Note: This method exists for compatibility but methods execute directly during interpret_stream. The returned list will contain None values (one per method call executed).

Source code in grammar-school-python/grammar_school/interpreter.py
def interpret(self, call_chain: CallChain) -> list[None]:
    """
    Interpret a CallChain by executing methods directly.

    Note: This method exists for compatibility but methods execute directly
    during interpret_stream. The returned list will contain None values
    (one per method call executed).
    """
    return list(self.interpret_stream(call_chain))

interpret_stream

interpret_stream(call_chain: CallChain)

Interpret a CallChain by executing methods directly (streaming).

This is a generator that executes methods one at a time, allowing for memory-efficient processing of large DSL programs.

Yields:

Name Type Description
None

One None per method executed (for compatibility with Action-based interface)

Source code in grammar-school-python/grammar_school/interpreter.py
def interpret_stream(self, call_chain: CallChain):
    """
    Interpret a CallChain by executing methods directly (streaming).

    This is a generator that executes methods one at a time, allowing
    for memory-efficient processing of large DSL programs.

    Yields:
        None: One None per method executed (for compatibility with Action-based interface)
    """
    for call in call_chain.calls:
        if call.name not in self._method_handlers:
            raise ValueError(f"Unknown method: {call.name}")

        handler = self._method_handlers[call.name]
        args = self._coerce_args(call.args)
        # Remove _context from args if present (methods don't need it)
        args.pop("_context", None)
        # Call method directly - it executes immediately
        handler(**args)
        # Yield None to indicate execution (for compatibility)
        yield None

Parser Backend

LarkBackend

LarkBackend(grammar: str = DEFAULT_GRAMMAR, transformer: Transformer | None = None, use_smart_transformer: bool = True)

Lark-based parser backend.

Initialize with a Lark grammar string.

Parameters:

Name Type Description Default
grammar str

Lark grammar string

DEFAULT_GRAMMAR
transformer Transformer | None

Optional custom transformer (if None, uses SmartTransformer or ASTTransformer)

None
use_smart_transformer bool

If True, use SmartTransformer (adapts to any grammar). If False, use ASTTransformer (coupled to default grammar).

True
Source code in grammar-school-python/grammar_school/backend_lark.py
def __init__(
    self,
    grammar: str = DEFAULT_GRAMMAR,
    transformer: Transformer | None = None,
    use_smart_transformer: bool = True,
):
    """
    Initialize with a Lark grammar string.

    Args:
        grammar: Lark grammar string
        transformer: Optional custom transformer (if None, uses SmartTransformer or ASTTransformer)
        use_smart_transformer: If True, use SmartTransformer (adapts to any grammar).
                             If False, use ASTTransformer (coupled to default grammar).
    """
    self.parser = Lark(grammar, start="start", parser="lalr")
    if transformer is not None:
        self.transformer = transformer
    elif use_smart_transformer:
        # SmartTransformer works with any grammar, including the default
        self.transformer = SmartTransformer()
    else:
        # ASTTransformer is faster for default grammar (backward compatibility)
        self.transformer = ASTTransformer()

Attributes

parser instance-attribute

parser = Lark(grammar, start='start', parser='lalr')

transformer instance-attribute

transformer = transformer

Functions

clean_grammar_for_cfg staticmethod

clean_grammar_for_cfg(grammar: str) -> str

Clean Lark grammar for use with CFG systems (e.g., GPT-5).

Removes Lark-specific directives that aren't supported in standard CFG: - %import directives - %ignore directives - Other %-prefixed directives

Parameters:

Name Type Description Default
grammar str

Lark grammar string with directives

required

Returns:

Type Description
str

Cleaned grammar string suitable for CFG systems

Example
cleaned = LarkBackend.clean_grammar_for_cfg(DEFAULT_GRAMMAR)
# Use cleaned grammar with GPT-5 CFG
Source code in grammar-school-python/grammar_school/backend_lark.py
@staticmethod
def clean_grammar_for_cfg(grammar: str) -> str:
    """
    Clean Lark grammar for use with CFG systems (e.g., GPT-5).

    Removes Lark-specific directives that aren't supported in standard CFG:
    - %import directives
    - %ignore directives
    - Other %-prefixed directives

    Args:
        grammar: Lark grammar string with directives

    Returns:
        Cleaned grammar string suitable for CFG systems

    Example:
        ```python
        cleaned = LarkBackend.clean_grammar_for_cfg(DEFAULT_GRAMMAR)
        # Use cleaned grammar with GPT-5 CFG
        ```
    """
    return "\n".join(line for line in grammar.split("\n") if not line.strip().startswith("%"))

parse

parse(code: str) -> CallChain

Parse code into a CallChain AST.

Source code in grammar-school-python/grammar_school/backend_lark.py
def parse(self, code: str) -> CallChain:
    """Parse code into a CallChain AST."""
    tree = self.parser.parse(code)
    result = self.transformer.transform(tree)
    # Handle case where transformer returns a list (unwrap it)
    if isinstance(result, list):
        if result and isinstance(result[0], CallChain):
            return result[0]
        # If list contains Calls, create a CallChain using iterator
        from grammar_school.ast import Call

        # Use generator expression instead of list comprehension for memory efficiency
        calls_iter = (item for item in result if isinstance(item, Call))
        calls = list(calls_iter)  # Convert to list for CallChain
        if calls:
            return CallChain(calls=calls)
        # Empty list - return empty CallChain
        return CallChain(calls=[])
    # Result should be a CallChain
    if isinstance(result, CallChain):
        return result
    # If it's a single Call, wrap it
    from grammar_school.ast import Call

    if isinstance(result, Call):
        return CallChain(calls=[result])
    # Fallback: return empty CallChain
    return CallChain(calls=[])

DEFAULT_GRAMMAR module-attribute

DEFAULT_GRAMMAR = '\nstart: statement+\n\n// Statement is a call chain (which can be a single call or multiple chained calls)\nstatement: call_chain\n\ncall_chain: call (DOT call)*\ncall: IDENTIFIER "(" args? ")"\nargs: arg (COMMA arg)*\narg: IDENTIFIER "=" expression\n    | expression\n\n// Expression with operator precedence (lowest to highest)\nexpression: comparison\ncomparison: addition (comparison_op addition)*\ncomparison_op: EQ | NE | LT | GT | LE | GE\naddition: multiplication (add_op multiplication)*\nadd_op: PLUS | MINUS\nmultiplication: atom (mul_op atom)*\nmul_op: MUL | DIV\natom: NUMBER\n    | STRING\n    | BOOL\n    | IDENTIFIER\n    | property_access\n    | function_ref\n    | "(" expression ")"\n\n// Property access: track.name\nproperty_access: IDENTIFIER (DOT IDENTIFIER)+\n\n// Function reference: @function_name syntax\nfunction_ref: "@" IDENTIFIER\n\n// Operators\nPLUS: "+"\nMINUS: "-"\nMUL: "*"\nDIV: "/"\nEQ: "=="\nNE: "!="\nLT: "<"\nGT: ">"\nLE: "<="\nGE: ">="\n\nDOT: "."\nCOMMA: ","\nNUMBER: /-?\\d+(\\.\\d+)?/\nSTRING: /"([^"\\\\]|\\\\.)*"|\'([^\'\\\\]|\\\\.)*\'/\nIDENTIFIER: /[a-zA-Z_][a-zA-Z0-9_]*/\nBOOL: "true" | "false"\n\n%import common.WS\n%ignore WS\n'

OpenAI CFG Utilities

Grammar School provides utilities for integrating with OpenAI's Context-Free Grammar (CFG) feature, allowing you to use Grammar School grammars as constraints for GPT-5.

CFGConfig

@dataclass
class CFGConfig:
    tool_name: str
    description: str
    grammar: str
    syntax: str = "lark"

Configuration for building an OpenAI CFG tool.

Example:

from grammar_school.openai_utils import CFGConfig

config = CFGConfig(
    tool_name="magda_dsl",
    description="Generates MAGDA DSL code for REAPER automation",
    grammar=grammar_string,
    syntax="lark",
)

build_openai_cfg_tool

def build_openai_cfg_tool(config: CFGConfig) -> dict[str, Any]

Builds an OpenAI CFG tool payload from a CFGConfig. This function: - Cleans the grammar using LarkBackend.clean_grammar_for_cfg() to remove unsupported Lark directives - Returns the properly formatted OpenAI tool structure - Ensures the syntax defaults to "lark" if not specified

Example:

from grammar_school.openai_utils import CFGConfig, build_openai_cfg_tool

tool = build_openai_cfg_tool(CFGConfig(
    tool_name="magda_dsl",
    description="Generates MAGDA DSL code for REAPER automation",
    grammar=grammar_string,
    syntax="lark",
))
# Add tool to OpenAI request: tools = [tool]

get_openai_text_format_for_cfg

def get_openai_text_format_for_cfg() -> dict[str, Any]

Returns the text format configuration that should be used when making OpenAI requests with CFG tools. When using CFG, the text format must be set to "text" (not JSON schema) because the output is DSL code, not JSON.

Example:

from grammar_school.openai_utils import get_openai_text_format_for_cfg

params["text"] = get_openai_text_format_for_cfg()

clean_grammar_for_cfg

@staticmethod
def LarkBackend.clean_grammar_for_cfg(grammar: str) -> str

Cleans a Lark grammar for use with CFG systems (e.g., GPT-5). Removes Lark-specific directives that aren't supported in standard CFG: - %import directives - %ignore directives - Other %-prefixed directives

CFGProvider Interface

Grammar School provides a CFGProvider interface for integrating with different LLM providers that support CFG. This allows you to use the same API with different LLM providers.

from abc import ABC, abstractmethod

class CFGProvider(ABC):
    @abstractmethod
    def build_tool(self, tool_name: str, description: str, grammar: str, syntax: str) -> dict[str, Any]:
        """Builds the vendor-specific CFG tool payload."""
        pass

    @abstractmethod
    def get_text_format(self) -> dict[str, Any]:
        """Returns the text format configuration for the vendor's API."""
        pass

    @abstractmethod
    def generate(self, prompt: str, model: str, tools: list[dict], text_format: dict, client=None, **kwargs) -> Any:
        """Generates DSL code using the vendor's LLM."""
        pass

    @abstractmethod
    def extract_dsl_code(self, response: Any) -> str:
        """Extracts DSL code from the vendor's response."""
        pass

OpenAICFGProvider

from grammar_school.cfg_vendor import OpenAICFGProvider

provider = OpenAICFGProvider()
cfg_tool = provider.build_tool(
    tool_name="task_dsl",
    description="Task management DSL",
    grammar=grammar_string,
    syntax="lark",
)
text_format = provider.get_text_format()

The OpenAICFGProvider class implements the CFGProvider interface for OpenAI's API. It handles: - Building OpenAI-specific CFG tool payloads - Configuring text format for CFG requests - Generating DSL code using OpenAI's API - Extracting DSL code from OpenAI responses

Example:

from grammar_school.cfg_vendor import OpenAICFGProvider
from openai import OpenAI

provider = OpenAICFGProvider()
cfg_tool = provider.build_tool(
    tool_name="task_dsl",
    description="Task management DSL",
    grammar=grammar.backend.grammar,
    syntax="lark",
)
text_format = provider.get_text_format()

client = OpenAI()
response = client.chat.completions.create(
    model="gpt-5",
    messages=[{"role": "user", "content": "Create a task"}],
    tools=[cfg_tool],
    tool_choice={"type": "required", "tool": {"name": "task_dsl"}},
    **text_format,
)

dsl_code = provider.extract_dsl_code(response)
grammar.execute(dsl_code)