Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d5c837a
non-breaking:
HendrikStrobelt Sep 24, 2025
1917b40
non-breaking:
HendrikStrobelt Sep 24, 2025
484ccf4
Context, SimpleContext, ChatContext (nee:LinearContext)
HendrikStrobelt Sep 24, 2025
a14b422
expand -> add
HendrikStrobelt Sep 25, 2025
2df9fbe
remove RootContext in favor of _is_root attribute.
HendrikStrobelt Sep 25, 2025
2fe2e79
.
HendrikStrobelt Sep 25, 2025
89428c9
actions_for_available_tools is back
HendrikStrobelt Sep 25, 2025
6359b41
all backends adjusted to new context structure.
HendrikStrobelt Sep 25, 2025
e64b51b
feat: add top level funcs and start sample changes
jakelorocco Sep 25, 2025
f3e9137
context adjustments and tests
HendrikStrobelt Sep 25, 2025
c9ec7df
maybe breaking:
HendrikStrobelt Sep 25, 2025
3646edf
untested session and sampling changes
HendrikStrobelt Sep 25, 2025
c2b3c47
backends should add ACTION and MOT.
HendrikStrobelt Sep 25, 2025
a47dae8
as_chat_history adapted
HendrikStrobelt Sep 25, 2025
849a7ed
fix: ollama client instantiate to prevent stale event loops in httpx …
jakelorocco Sep 26, 2025
c3854f4
fix: mypy issues
jakelorocco Sep 26, 2025
0754a5d
Renaming:
HendrikStrobelt Sep 26, 2025
590a285
name cleanup
HendrikStrobelt Sep 26, 2025
e494305
adding contexts to sampling
HendrikStrobelt Sep 26, 2025
d0f6633
1) handling sampling results and context
HendrikStrobelt Sep 26, 2025
5e005bd
adding warning
HendrikStrobelt Sep 26, 2025
b94809b
name clarity
HendrikStrobelt Sep 26, 2025
9a2efbf
remove unnecessary function
HendrikStrobelt Sep 26, 2025
507d10c
updated tests:
HendrikStrobelt Sep 26, 2025
ede690e
fix: watsonx errors
jakelorocco Sep 26, 2025
4afa7cf
fix: sampling and vision tests
jakelorocco Sep 26, 2025
09a34b6
examples react - error to be looked into by @JAL
HendrikStrobelt Sep 26, 2025
3ca56a3
fixed react - thanks to @JAL
HendrikStrobelt Sep 26, 2025
597332c
examples for alora (untested) and gen slots (tested)
HendrikStrobelt Sep 26, 2025
6a137f4
more examples updated
HendrikStrobelt Sep 26, 2025
5b11c5c
more examples updated
HendrikStrobelt Sep 26, 2025
81446e9
remove old context
HendrikStrobelt Sep 26, 2025
f483afa
fix: best of n and misc changes
jakelorocco Sep 27, 2025
09a50b2
fix: improve runtime of best of n sampling
jakelorocco Sep 27, 2025
5fbf63e
fix: add tests
jakelorocco Sep 27, 2025
d1c7fb5
fix: remove references to LinearContext
jakelorocco Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/examples/aLora/101_example.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import time

from mellea import LinearContext, MelleaSession
from mellea import MelleaSession
from mellea.backends.aloras.huggingface.granite_aloras import HFConstraintAlora
from mellea.backends.cache import SimpleLRUCache
from mellea.backends.huggingface import LocalHFBackend
from mellea.stdlib.base import GenerateLog
from mellea.stdlib.base import ChatContext, GenerateLog
from mellea.stdlib.requirement import ALoraRequirement, Requirement

# Define a backend and add the constraint aLora
Expand All @@ -22,7 +22,7 @@
backend.add_alora(custom_stembolt_failure_constraint)

# Create M session
m = MelleaSession(backend, ctx=LinearContext())
m = MelleaSession(backend, ctx=ChatContext())

# define a requirement
failure_check = ALoraRequirement(
Expand Down
16 changes: 9 additions & 7 deletions docs/examples/agents/react.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import inspect
import json
from collections.abc import Callable
from typing import Literal, Unpack
from typing import Literal

import pydantic
from jinja2 import Template
Expand All @@ -13,6 +13,7 @@
import mellea.stdlib
import mellea.stdlib.base
import mellea.stdlib.chat
from mellea.stdlib.base import ChatContext

react_system_template: Template = Template(
"""Answer the user's question as best you can.
Expand Down Expand Up @@ -83,7 +84,7 @@ def call_tool(self, tool: ReactTool, kwargs_json: str):
def tool_name_schema(self):
names = self.tool_names()
fields = dict()
fields["tool"] = Literal[Unpack[names]]
fields["tool"] = Literal[*names]
return pydantic.create_model("ToolSelectionSchema", **fields)

def get_tool_from_schema(self, content: str):
Expand All @@ -103,7 +104,7 @@ def react(
react_toolbox: ReactToolbox,
):
assert m.ctx.is_chat_context, "ReACT requires a chat context."
test_ctx_lin = m.ctx.render_for_generation()
test_ctx_lin = m.ctx.view_for_generation()
assert test_ctx_lin is not None and len(test_ctx_lin) == 0, (
"ReACT expects a fresh context."
)
Expand All @@ -114,8 +115,9 @@ def react(
)

# Add the system prompt and the goal to the chat history.
m.ctx.insert(mellea.stdlib.chat.Message(role="system", content=_sys_prompt))
m.ctx.insert(mellea.stdlib.chat.Message(role="user", content=f"{goal}"))
m.ctx = m.ctx.add(
mellea.stdlib.chat.Message(role="system", content=_sys_prompt)
).add(mellea.stdlib.chat.Message(role="user", content=f"{goal}"))

# The main ReACT loop as a dynamic program:
# ( ?(not done) ;
Expand Down Expand Up @@ -156,7 +158,7 @@ def react(

print("### Observation")
tool_output = react_toolbox.call_tool(selected_tool, act_args.content)
m.ctx.insert(mellea.stdlib.chat.Message(role="tool", content=tool_output))
m.ctx = m.ctx.add(mellea.stdlib.chat.Message(role="tool", content=tool_output))
print(tool_output)

print("### Done Check")
Expand All @@ -178,7 +180,7 @@ def react(


if __name__ == "__main__":
m = mellea.start_session(ctx=mellea.stdlib.base.LinearContext())
m = mellea.start_session(ctx=ChatContext())

def zip_lookup_tool_fn(city: str):
"""Returns the ZIP code for the `city`."""
Expand Down
16 changes: 9 additions & 7 deletions docs/examples/agents/react_instruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import inspect
import json
from collections.abc import Callable
from typing import Literal, Unpack
from typing import Literal

import pydantic
from jinja2 import Template
Expand All @@ -11,6 +11,7 @@
import mellea.stdlib
import mellea.stdlib.base
import mellea.stdlib.chat
from mellea.stdlib.base import ChatContext

react_system_template: Template = Template(
"""Answer the user's question as best you can.
Expand Down Expand Up @@ -81,7 +82,7 @@ def call_tool(self, tool: ReactTool, kwargs_json: str):
def tool_name_schema(self):
names = self.tool_names()
fields = dict()
fields["tool"] = Literal[Unpack[names]]
fields["tool"] = Literal[*names]
return pydantic.create_model("ToolSelectionSchema", **fields)

def get_tool_from_schema(self, content: str):
Expand All @@ -101,7 +102,7 @@ def react(
react_toolbox: ReactToolbox,
):
assert m.ctx.is_chat_context, "ReACT requires a chat context."
test_ctx_lin = m.ctx.render_for_generation()
test_ctx_lin = m.ctx.view_for_generation()
assert test_ctx_lin is not None and len(test_ctx_lin) == 0, (
"ReACT expects a fresh context."
)
Expand All @@ -112,8 +113,9 @@ def react(
)

# Add the system prompt and the goal to the chat history.
m.ctx.insert(mellea.stdlib.chat.Message(role="system", content=_sys_prompt))
m.ctx.insert(mellea.stdlib.chat.Message(role="user", content=f"{goal}"))
m.ctx = m.ctx.add(
mellea.stdlib.chat.Message(role="system", content=_sys_prompt)
).add(mellea.stdlib.chat.Message(role="user", content=f"{goal}"))

# The main ReACT loop as a dynamic program:
# ( ?(not done) ;
Expand Down Expand Up @@ -159,7 +161,7 @@ def react(

print("### Observation")
tool_output = react_toolbox.call_tool(selected_tool, act_args_val)
m.ctx.insert(mellea.stdlib.chat.Message(role="tool", content=tool_output))
m.ctx = m.ctx.add(mellea.stdlib.chat.Message(role="tool", content=tool_output))
print(tool_output)

print("### Done Check")
Expand Down Expand Up @@ -187,7 +189,7 @@ def react(


if __name__ == "__main__":
m = mellea.start_session(ctx=mellea.stdlib.base.LinearContext())
m = mellea.start_session(ctx=ChatContext())

def zip_lookup_tool_fn(city: str):
"""Returns the ZIP code for the `city`."""
Expand Down
10 changes: 5 additions & 5 deletions docs/examples/generative_slots/generate_with_context.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from mellea import LinearContext, generative, start_session
from mellea import generative, start_session
from mellea.backends.types import ModelOption
from mellea.stdlib.base import CBlock
from mellea.stdlib.base import CBlock, ChatContext

# Generative slots can be used with sessions that have context.
# By utilizing context, you can change the results of several
Expand Down Expand Up @@ -34,7 +34,7 @@ def give_feedback(essay: str) -> list[str]:

if __name__ == "__main__":
m = start_session(
ctx=LinearContext(), model_options={ModelOption.MAX_NEW_TOKENS: 100}
ctx=ChatContext(), model_options={ModelOption.MAX_NEW_TOKENS: 100}
)

text = """
Expand All @@ -55,7 +55,7 @@ def give_feedback(essay: str) -> list[str]:

# If you have a set of generative functions, you can tweak them all by
# adding context to the session they are running in.
m.ctx.insert(
m.ctx = m.ctx.add(
CBlock(
"You are an elementary school teacher. "
"Any grades and feedback that you give should keep that in mind. Remember to be "
Expand All @@ -74,7 +74,7 @@ def give_feedback(essay: str) -> list[str]:

# And, let's reset the context and try a different grading style.
m.reset()
m.ctx.insert(
m.ctx = m.ctx.add(
CBlock(
"You are a grammarian that is focused solely on spelling and syntax, "
"not on the content of essays. When giving grades and feedback, focus "
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/helper/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .helpers import Any, fill, w
from .helpers import req_print, w
7 changes: 7 additions & 0 deletions docs/examples/helper/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from textwrap import fill
from typing import Any

from mellea.stdlib.requirement import Requirement, ValidationResult


# Just for printing stuff nicely...
def w(x: Any) -> str:
return fill(str(x), width=120, replace_whitespace=False)


def req_print(rv_list: list[tuple[Requirement, ValidationResult]]) -> str:
parts = [f"{bool(rv[1])}\t: {rv[0].description}" for rv in rv_list]
return "\n".join(parts)
8 changes: 4 additions & 4 deletions docs/examples/image_text_models/vision_ollama_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

from PIL import Image

from mellea import LinearContext, start_session
from mellea.stdlib.base import ImageBlock
from mellea import start_session
from mellea.stdlib.base import ChatContext, ImageBlock

m = start_session(model_id="granite3.2-vision", ctx=LinearContext())
# m = start_session(model_id="llava", ctx=LinearContext())
m = start_session(model_id="granite3.2-vision", ctx=ChatContext())
# m = start_session(model_id="llava", ctx=ChatContext())

# load image
test_img = Image.open("pointing_up.jpg")
Expand Down
15 changes: 2 additions & 13 deletions docs/examples/instruct_validate_repair/101_email.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
# This is the 101 example for using `session` and `instruct`.
# helper function to wrap text
from docs.examples.helper import w
from mellea import instruct, start_session
from mellea import start_session
from mellea.backends.types import ModelOption

# create a session using Granite 3.3 8B on Ollama and a simple context [see below]
with start_session(model_options={ModelOption.MAX_NEW_TOKENS: 200}):
# write an email
email_v1 = instruct("Write an email to invite all interns to the office party.")

with start_session(model_options={ModelOption.MAX_NEW_TOKENS: 200}) as m:
# write an email
email_v1 = m.instruct("Write an email to invite all interns to the office party.")
print(m.last_prompt())

# print result
print(f"***** email ****\n{w(email_v1)}\n*******")

# ************** END *************


# # optionally: print the debug log for the last instruction on the context
# from mellea.stdlib.base import GenerateLog
# _, log = m.ctx.last_output_and_logs()
# if isinstance(log, GenerateLog): # should be
# print(f"Prompt:\n{w(log.prompt)}") # print prompt

# # start_session() is equivalent to:
# from mellea.backends import model_ids
# from mellea.backends.ollama import OllamaModelBackend
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# create a session using Granite 3.3 8B on Ollama and a simple context [see below]
m = start_session(model_options={ModelOption.MAX_NEW_TOKENS: 200})

# write an email
# write an email with automatic requirement checking.
email_v1 = m.instruct(
"Write an email to invite all interns to the office party.",
requirements=["be formal", "Use 'Dear interns' as greeting."],
Expand Down
18 changes: 14 additions & 4 deletions docs/examples/instruct_validate_repair/101_email_with_validate.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
from docs.examples.helper import w
from docs.examples.helper import req_print, w
from mellea import start_session
from mellea.backends.types import ModelOption
from mellea.stdlib.sampling import RejectionSamplingStrategy

# create a session using Granite 3.3 8B on Ollama and a simple context [see below]
m = start_session(model_options={ModelOption.MAX_NEW_TOKENS: 200})

email_v1 = m.instruct(
email_v2_samples = m.instruct(
"Write an email to invite all interns to the office party.",
requirements=["be formal", "Use 'Dear interns' as greeting."],
strategy=RejectionSamplingStrategy(loop_budget=3),
return_sampling_results=True,
)

# print result
print(f"***** email ****\n{w(email_v1)}\n*******")
if email_v2_samples.success:
print(f"Success: \n{w(email_v2_samples.result)}")
print(
f"===> Requirement for this sample: \n{req_print(email_v2_samples.sample_validations[-1])}"
)
else:
print(f"Failure: \n{w(email_v2_samples.result)}")
selected_index = email_v2_samples.sample_generations.index(email_v2_samples.result)
print(
f"===> Requirement for this sample: \n{req_print(email_v2_samples.sample_validations[selected_index])}"
)

# # [optional] get logs for all loops:
# from mellea.stdlib.base import GenerateLog
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/mify/rich_document_advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from mellea.stdlib.base import ModelOutputThunk, TemplateRepresentation

# Use a `SimpleContext` so that each LLM call is independent.
m = mellea.start_session(backend_name="hf", ctx=mellea.SimpleContext())
m = mellea.start_session(backend_name="hf")

# 2. Let's import docling so that we can process pdf documents.

Expand Down
7 changes: 4 additions & 3 deletions docs/examples/notebooks/context_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"source": [
"## Import Mellea and Start a Session with LinearContext\n",
"\n",
"Up to this point we have used SimpleContext, a context manager that resets the chat message history on each model call. That is, the model's context is entirely determined by the current Component. \n",
"Up to this point we have used SimpleContext, a context manager that resets the chat message history on each model call. That is, the model's context is entirely determined by the current Component.\n",
"\n",
"Mellea also provides a LinearContext, which behaves like a chat history. We will use the LinearContext to interact with cat hmodels:"
]
Expand All @@ -84,9 +84,10 @@
},
"outputs": [],
"source": [
"from mellea import LinearContext, start_session\n",
"from mellea import start_session\n",
"from mellea.stdlib.base import ChatContext\n",
"\n",
"m = start_session(ctx=LinearContext())\n",
"m = start_session(ctx=ChatContext())\n",
"m.chat(\"Make up a math problem.\")\n",
"m.chat(\"Solve your math problem.\")\n",
"print(m.ctx.last_output())\n",
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/notebooks/m_serve_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@
"\n",
"import mellea\n",
"from cli.serve.models import ChatMessage\n",
"from mellea.stdlib.base import LinearContext, ModelOutputThunk\n",
"from mellea.stdlib.base import ChatContext, ModelOutputThunk\n",
"from mellea.stdlib.requirement import Requirement, simple_validate\n",
"from mellea.stdlib.sampling import RejectionSamplingStrategy, SamplingResult\n",
"\n",
"session = mellea.start_session(ctx=LinearContext())\n",
"session = mellea.start_session(ctx=ChatContext())\n",
"\n",
"\n",
"def validate_hi_bob(email: str) -> bool:\n",
Expand Down
8 changes: 3 additions & 5 deletions docs/examples/safety.py/guardian.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

from mellea import MelleaSession
from mellea.backends import model_ids
from mellea.backends.dummy import DummyBackend
from mellea.backends.ollama import OllamaModelBackend
from mellea.stdlib.base import Context, ContextTurn, ModelOutputThunk, SimpleContext
from mellea.stdlib.base import ContextTurn, ModelOutputThunk
from mellea.stdlib.chat import Message
from mellea.stdlib.safety.guardian import GuardianCheck, GuardianRisk

Expand All @@ -25,10 +24,9 @@
print("\n Test 2\n")

# create a mean conversation and add to context
m.ctx.insert_turn(
ContextTurn(Message("user", "Hello. "), ModelOutputThunk("You are very ugly."))
m.ctx = m.ctx.add(Message("user", "Hello. ")).add(
ModelOutputThunk("You are very ugly.")
)

# show last turn in chat
print(f"Context: {m.ctx.last_turn()}")

Expand Down
4 changes: 2 additions & 2 deletions docs/examples/sessions/creating_a_new_type_of_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from mellea import MelleaSession
from mellea.backends import Backend, BaseModelSubclass
from mellea.backends.ollama import OllamaModelBackend
from mellea.stdlib.base import CBlock, Context, LinearContext, ModelOutputThunk
from mellea.stdlib.base import CBlock, ChatContext, Context, ModelOutputThunk
from mellea.stdlib.chat import Message
from mellea.stdlib.requirement import Requirement, reqify
from mellea.stdlib.safety.guardian import GuardianCheck, GuardianRisk
Expand Down Expand Up @@ -66,7 +66,7 @@ def chat(
m = ChatCheckingSession(
requirements=[GuardianCheck("jailbreak"), GuardianCheck("profanity")],
backend=OllamaModelBackend(),
ctx=LinearContext(),
ctx=ChatContext(),
)

# You can run this code to see the immediate checks working.
Expand Down
7 changes: 4 additions & 3 deletions docs/examples/tutorial/context_example.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from mellea import LinearContext, start_session
from mellea import start_session
from mellea.stdlib.base import ChatContext

m = start_session(ctx=LinearContext())
m = start_session(ctx=ChatContext())
m.chat("Make up a math problem.")
m.chat("Solve your math problem.")

print(m.ctx.last_output())

print("==================")
print(m.ctx.last_turn())
Loading