Bifrost

class Bifrost(*, llm: LLMIntegration, prompt_envelope: heimdallm.envelope.PromptEnvelope, grammar: Lark, tree_producer: Callable[[Lark, str], Tree[Token]], constraint_validators: Sequence[heimdallm.constraints.ConstraintValidator])

The Bifrost is the bridge from the outside world to your secure systems. It is responsible for a rigorous parsing and validation of the output of the LLM. This class is not used directly, but is subclassed to provide a Bifrost for a specific problem, like constructing trusted SQL queries. It is general enough to be adapted to other problems.

Parameters:
  • llm (LLMIntegration) – The LLM integration to use.

  • prompt_envelope (heimdallm.envelope.PromptEnvelope) – The prompt envelope used to wrap the untrusted human input and unwrap the untrusted LLM output.

  • grammar (Lark) – The grammar that defines the structured output that we expect from the LLM.

  • tree_producer (Callable[[Lark, str], Tree[Token]]) – A callable that takes a grammar and the untrusted input and returns a parse tree. Using a callback allows us to do Bifrost-specific parsing, for example, to collapse ambiguous parse trees into a single parse tree.

  • constraint_validators (Sequence[heimdallm.constraints.ConstraintValidator]) – A sequence of constraint validators that will be used to validate the parse tree returned by the tree_producer. Only one validator needs to succeed for validation to pass.

parse(untrusted_llm_output: str) Tree[Token]

Converts the LLM output into a parse tree. Override it in a subclass to throw custom exceptions based on the grammar and parse state.

Parameters:

untrusted_llm_output (str) – The unwrapped output from the LLM.

Returns:

The parse tree.

Return type:

Tree[Token]

post_transform(trusted_llm_output: str, tree: Tree[Token]) str

A hook for subclasses to perform post-transformations on the trusted output. This is useful for making adjustments that cannot be made during 🧩 Reconstruction because they would produce an output that is incompatible with the grammar.

For example, replacing the generic :placeholder with the SQL-specific placeholder fields (e.g. %(placeholder)s) cannot be done in reconstruction because it would conflict with the grammar. It needs to be done in a separate step, after the input has been reconstruction and the constraint validators have been satisfied.

Parameters:
  • trusted_llm_output (str) –

  • tree (Tree[Token]) –

Return type:

str

traverse(untrusted_human_input: str, autofix: bool = True) str

Run the full chain of the Bifrost, from untrusted input to trusted input. Traversing the Bifrost means successfully returning a value from this function, which is only possible if every step succeeds.

This is the main entry point for the Bifrost API.

Parameters:
  • untrusted_human_input (str) – The untrusted input from the user.

  • autofix (bool) – Whether or not to attempt to reconstruct the input to satisfy the constraint validator.

Returns:

The trusted LLM output.

Return type:

str

classmethod validation_only(constraint_validators: Any | Sequence[Any])

A convenience method for doing just static analysis. This creates a Bifrost that assumes its untrusted input was already produced by an LLM, so we only need to parse and validate it.

Parameters:

constraint_validators (Any | Sequence[Any]) – A constraint validator or sequence of constraint validators to run on the untrusted input.