Concept overview: MObjects and mify explains the design and trade-offs.This tutorial shows how to make existing Python objects queryable and transformable by the LLM using
@mify — without changing their Python interface or behaviour.
By the end you will have covered:
- Applying
@mifyto an existing class m.query()— ask questions about an objectm.transform()— produce a transformed version of an object- Controlling which fields and methods the LLM sees
- Using
stringify_funcfor custom text representations
pip install mellea, Ollama running locally with granite4.1:3b downloaded.
The scenario
You have aCustomerRecord class — existing code that you cannot rewrite. You want
to start asking the LLM questions about individual records and generating
personalised summaries.
Step 1: Apply @mify
Decorate the class with@mify. This adds the LLM-queryable protocol to every
instance, without touching the class’s Python interface:
Sample output
Note: LLM output is non-deterministic, output may vary.
@mify adds the MObject protocol to every instance. The
stringify_func controls the text the LLM receives. It is required here because
without it the model sees Python’s default str() repr — which for a custom class
contains no field values — and cannot answer questions about the object’s data.
Full example: docs/examples/mify/mify.py
Step 2: Control the text representation
Refine thestringify_func to produce well-labelled, human-readable text that
gives the model the clearest possible view of the object:
Sample output
Note: LLM output is non-deterministic, output may vary.
Step 3: Limit which fields are visible
To hide internal state from the LLM, usefields_include with a Jinja2 template:
Sample output
Note: LLM output is non-deterministic, output may vary. The model only seesThenameandspend_ytd—last_purchaseis excluded byfields_includeand will never appear in the response.
last_purchase field is not in fields_include so it is never sent to the
model.
Step 4: Use m.transform()
m.transform() asks the LLM to produce a modified version of the object by
calling one of its methods. Expose the target method with funcs_include:
Sample output
Note:The LLM callsm.transform()returns whateverto_summary()returns — here a newCustomerRecordconstructed with the generated text asname. Access.nameto retrieve it. Your wording will vary.
to_summary(summary=...) with the generated text, and the return
value of that method is the result.
Step 5: Mify an object ad hoc
You can also mify an existing object instance without decorating its class — useful when you don’t own the class definition:Sample output
Note: LLM output is non-deterministic, output may vary. Whenmifyis called on an instance,stringify_funcmust be a zero-argument callable that closes over the instance (as above). This differs from the class-level@mify(stringify_func=lambda r: ...)form, where Python’s method binding passes the instance as the first argument. Usinglambda r:on an instance will raise aTypeErrorat query time.
What you built
A set of patterns for making legacy Python objects LLM-queryable without modifying their class definitions:
See also: MObjects and mify |
Working with Data |
Tutorial 03: Using Generative Stubs