Skip to content

Commit 03e8b01

Browse files
committed
Merge remote-tracking branch 'origin/main' into tests-isolation
2 parents 4a28977 + f92a286 commit 03e8b01

26 files changed

+3162
-2627
lines changed

.github/workflows/quality.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Verify Code Quality
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.ref_name }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
quality:
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 30
17+
strategy:
18+
matrix:
19+
python-version: ['3.10', '3.11', '3.12'] # Need to add 3.13 once we resolve outlines issues.
20+
steps:
21+
- uses: actions/checkout@v4
22+
- name: Install uv and set the python version
23+
uses: astral-sh/setup-uv@v5
24+
with:
25+
python-version: ${{ matrix.python-version }}
26+
enable-cache: true
27+
- name: pre-commit cache key
28+
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> "$GITHUB_ENV"
29+
- uses: actions/cache@v4
30+
with:
31+
path: ~/.cache/pre-commit
32+
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
33+
- name: Install dependencies
34+
run: uv sync --frozen --all-extras
35+
- name: Check style and run tests
36+
run: pre-commit run --all-files
37+
- name: Send failure message
38+
if: failure() # This step will only run if a previous step failed
39+
run: echo "The quality verification failed. Please run precommit "

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,30 @@ You can get started with a local install, or by using Colab notebooks.
4747

4848
<img src="https://github.com/generative-computing/mellea/raw/main/docs/GetStarted_py.png" style="max-width:800px">
4949

50-
Install with pip:
50+
Install with [uv](https://docs.astral.sh/uv/getting-started/installation/):
5151

5252
```bash
5353
uv pip install mellea
5454
```
5555

56+
Install with pip:
57+
58+
```bash
59+
pip install mellea
60+
```
61+
62+
> [!NOTE]
63+
> `mellea` comes with some additional packages as defined in our `pyproject.toml`. I you would like to install all the extra optional dependencies, please run the following commands:
64+
>
65+
> ```bash
66+
> uv pip install mellea[hf] # for Huggingface extras and Alora capabilities.
67+
> uv pip install mellea[watsonx] # for watsonx backend
68+
> uv pip install mellea[docling] # for docling
69+
> uv pip install mellea[all] # for all the optional dependencies
70+
> ```
71+
>
72+
> You can also install all the optional dependencies with `uv sync --all-extras`
73+
5674
> [!NOTE]
5775
> If running on an Intel mac, you may get errors related to torch/torchvision versions. Conda maintains updated versions of these packages. You will need to create a conda environment and run `conda install 'torchvision>=0.22.0'` (this should also install pytorch and torchvision-extra). Then, you should be able to run `uv pip install mellea`. To run the examples, you will need to use `python <filename>` inside the conda environment instead of `uv run --with mellea <filename>`.
5876
@@ -110,7 +128,19 @@ uv venv .venv && source .venv/bin/activate
110128
Use `uv pip` to install from source with the editable flag:
111129
112130
```bash
113-
uv pip install -e .
131+
uv pip install -e .[all]
132+
```
133+
134+
If you are planning to contribute to the repo, it would be good to have all the development requirements installed:
135+
136+
```bash
137+
uv pip install .[all] --group dev --group notebook --group docs
138+
```
139+
140+
or
141+
142+
```bash
143+
uv sync --all-extras --all-groups
114144
```
115145
116146
Ensure that you install the precommit hooks:

docs/dev/constrained_decoding.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The `m` framework currently uses the `format` argument to pydantic schemas, **ou
1010

1111
> If a keyword had meaning across multiple types of backends, and if it means the same thing in all of those backends but has different names, then we use the `@@@`-style args so that the user can pass these args across all backends in the same way. Otherwise, the arguments in model_args are passed along verbatim.
1212
13-
This argues for `@@@format@@@` as opposed to a dedicated `format` option in the method signature. Or, in the alternative, for an entir re-think of ModelArgs.
13+
This argues for `@@@format@@@` as opposed to a dedicated `format` option in the method signature. Or, in the alternative, for an entire re-think of ModelArgs.
1414

1515
## Integration with grammar-targeted LLMs
1616

docs/examples/generative_slots/generative_slots.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,19 @@ def generate_summary(text: str) -> str:
1616

1717

1818
if __name__ == "__main__":
19-
m = start_session()
20-
sentiment_component = classify_sentiment(m, text="I love this!")
21-
print("Output sentiment is : ", sentiment_component)
22-
23-
summary = generate_summary(
24-
m,
25-
text="""
26-
The eagle rays are a group of cartilaginous fishes in the family Myliobatidae,
27-
consisting mostly of large species living in the open ocean rather than on the sea bottom.
28-
Eagle rays feed on mollusks, and crustaceans, crushing their shells with their flattened teeth.
29-
They are excellent swimmers and are able to breach the water up to several meters above the
30-
surface. Compared with other rays, they have long tails, and well-defined, rhomboidal bodies.
31-
They are ovoviviparous, giving birth to up to six young at a time. They range from 0.48 to
32-
5.1 m (1.6 to 16.7 ft) in length and 7 m (23 ft) in wingspan.
33-
""",
34-
)
35-
print("Generated summary is :", summary)
19+
with start_session():
20+
sentiment_component = classify_sentiment(text="I love this!")
21+
print("Output sentiment is : ", sentiment_component)
22+
23+
summary = generate_summary(
24+
text="""
25+
The eagle rays are a group of cartilaginous fishes in the family Myliobatidae,
26+
consisting mostly of large species living in the open ocean rather than on the sea bottom.
27+
Eagle rays feed on mollusks, and crustaceans, crushing their shells with their flattened teeth.
28+
They are excellent swimmers and are able to breach the water up to several meters above the
29+
surface. Compared with other rays, they have long tails, and well-defined, rhomboidal bodies.
30+
They are ovoviviparous, giving birth to up to six young at a time. They range from 0.48 to
31+
5.1 m (1.6 to 16.7 ft) in length and 7 m (23 ft) in wingspan.
32+
"""
33+
)
34+
print("Generated summary is :", summary)

docs/examples/instruct_validate_repair/101_email.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
# This is the 101 example for using `session` and `instruct`.
22
# helper function to wrap text
33
from docs.examples.helper import w
4-
from mellea import start_session
4+
from mellea import instruct, start_session
55
from mellea.backends.types import ModelOption
66

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

10-
# write an email
11-
email_v1 = m.instruct("Write an email to invite all interns to the office party.")
12+
with start_session(model_options={ModelOption.MAX_NEW_TOKENS: 200}) as m:
13+
# write an email
14+
email_v1 = m.instruct("Write an email to invite all interns to the office party.")
1215

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

docs/examples/notebooks/compositionality_with_generative_slots.ipynb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@
3333
"!nohup ollama serve >/dev/null 2>&1 &\n",
3434
"\n",
3535
"from IPython.display import HTML, display\n",
36-
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
37-
"get_ipython().events.register('pre_run_cell',set_css)"
36+
"\n",
37+
"\n",
38+
"def set_css():\n",
39+
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
40+
"\n",
41+
"\n",
42+
"get_ipython().events.register(\"pre_run_cell\", set_css)"
3843
]
3944
},
4045
{
@@ -76,15 +81,18 @@
7681
"source": [
7782
"from mellea import generative\n",
7883
"\n",
84+
"\n",
7985
"# The Summarizer Library\n",
8086
"@generative\n",
8187
"def summarize_meeting(transcript: str) -> str:\n",
8288
" \"\"\"Summarize the meeting transcript into a concise paragraph of main points.\"\"\"\n",
8389
"\n",
90+
"\n",
8491
"@generative\n",
8592
"def summarize_contract(contract_text: str) -> str:\n",
8693
" \"\"\"Produce a natural language summary of contract obligations and risks.\"\"\"\n",
8794
"\n",
95+
"\n",
8896
"@generative\n",
8997
"def summarize_short_story(story: str) -> str:\n",
9098
" \"\"\"Summarize a short story, with one paragraph on plot and one paragraph on braod themes.\"\"\""
@@ -109,10 +117,12 @@
109117
"def propose_business_decision(summary: str) -> str:\n",
110118
" \"\"\"Given a structured summary with clear recommendations, propose a business decision.\"\"\"\n",
111119
"\n",
120+
"\n",
112121
"@generative\n",
113122
"def generate_risk_mitigation(summary: str) -> str:\n",
114123
" \"\"\"If the summary contains risk elements, propose mitigation strategies.\"\"\"\n",
115124
"\n",
125+
"\n",
116126
"@generative\n",
117127
"def generate_novel_recommendations(summary: str) -> str:\n",
118128
" \"\"\"Provide a list of novel recommendations that are similar in plot or theme to the short story summary.\"\"\""
@@ -135,16 +145,19 @@
135145
"outputs": [],
136146
"source": [
137147
"# Compose the libraries.\n",
138-
"from typing import Literal # noqa: E402\n",
148+
"from typing import Literal\n",
149+
"\n",
139150
"\n",
140151
"@generative\n",
141152
"def has_structured_conclusion(summary: str) -> Literal[\"yes\", \"no\"]:\n",
142153
" \"\"\"Determine whether the summary contains a clearly marked conclusion or recommendation.\"\"\"\n",
143154
"\n",
155+
"\n",
144156
"@generative\n",
145157
"def contains_actionable_risks(summary: str) -> Literal[\"yes\", \"no\"]:\n",
146158
" \"\"\"Check whether the summary contains references to business risks or exposure.\"\"\"\n",
147159
"\n",
160+
"\n",
148161
"@generative\n",
149162
"def has_theme_and_plot(summary: str) -> Literal[\"yes\", \"no\"]:\n",
150163
" \"\"\"Check whether the summary contains both a plot and thematic elements.\"\"\""
@@ -166,7 +179,7 @@
166179
},
167180
"outputs": [],
168181
"source": [
169-
"from mellea import start_session # noqa: E402\n",
182+
"from mellea import start_session\n",
170183
"\n",
171184
"m = start_session()"
172185
]

docs/examples/notebooks/context_example.ipynb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@
3333
"!nohup ollama serve >/dev/null 2>&1 &\n",
3434
"\n",
3535
"from IPython.display import HTML, display\n",
36-
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
37-
"get_ipython().events.register('pre_run_cell',set_css)"
36+
"\n",
37+
"\n",
38+
"def set_css():\n",
39+
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
40+
"\n",
41+
"\n",
42+
"get_ipython().events.register(\"pre_run_cell\", set_css)"
3843
]
3944
},
4045
{
@@ -80,6 +85,7 @@
8085
"outputs": [],
8186
"source": [
8287
"from mellea import LinearContext, start_session\n",
88+
"\n",
8389
"m = start_session(ctx=LinearContext())\n",
8490
"m.chat(\"Make up a math problem.\")\n",
8591
"m.chat(\"Solve your math problem.\")\n",

docs/examples/notebooks/document_mobject.ipynb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@
3333
"!nohup ollama serve >/dev/null 2>&1 &\n",
3434
"\n",
3535
"from IPython.display import HTML, display\n",
36-
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
37-
"get_ipython().events.register('pre_run_cell',set_css)"
36+
"\n",
37+
"\n",
38+
"def set_css():\n",
39+
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
40+
"\n",
41+
"\n",
42+
"get_ipython().events.register(\"pre_run_cell\", set_css)"
3843
]
3944
},
4045
{
@@ -77,6 +82,7 @@
7782
"outputs": [],
7883
"source": [
7984
"from mellea.stdlib.docs.richdocument import RichDocument\n",
85+
"\n",
8086
"rd = RichDocument.from_document_file(\"https://arxiv.org/pdf/1906.04043\")"
8187
]
8288
},
@@ -96,7 +102,8 @@
96102
},
97103
"outputs": [],
98104
"source": [
99-
"from mellea.stdlib.docs.richdocument import Table # noqa: E402\n",
105+
"from mellea.stdlib.docs.richdocument import Table\n",
106+
"\n",
100107
"table1: Table = rd.get_tables()[0]\n",
101108
"print(table1.to_markdown())"
102109
]
@@ -119,8 +126,9 @@
119126
},
120127
"outputs": [],
121128
"source": [
122-
"from mellea import start_session # noqa: E402\n",
123-
"from mellea.backends.types import ModelOption # noqa: E402\n",
129+
"from mellea import start_session\n",
130+
"from mellea.backends.types import ModelOption\n",
131+
"\n",
124132
"m = start_session()\n",
125133
"for seed in [x * 12 for x in range(5)]:\n",
126134
" table2 = m.transform(\n",

docs/examples/notebooks/example.ipynb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@
3333
"!nohup ollama serve >/dev/null 2>&1 &\n",
3434
"\n",
3535
"from IPython.display import HTML, display\n",
36-
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
37-
"get_ipython().events.register('pre_run_cell',set_css)"
36+
"\n",
37+
"\n",
38+
"def set_css():\n",
39+
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
40+
"\n",
41+
"\n",
42+
"get_ipython().events.register(\"pre_run_cell\", set_css)"
3843
]
3944
},
4045
{
@@ -77,6 +82,7 @@
7782
"outputs": [],
7883
"source": [
7984
"import mellea\n",
85+
"\n",
8086
"m = mellea.start_session()"
8187
]
8288
},

docs/examples/notebooks/instruct_validate_repair.ipynb

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@
3333
"!nohup ollama serve >/dev/null 2>&1 &\n",
3434
"\n",
3535
"from IPython.display import HTML, display\n",
36-
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
37-
"get_ipython().events.register('pre_run_cell',set_css)"
36+
"\n",
37+
"\n",
38+
"def set_css():\n",
39+
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
40+
"\n",
41+
"\n",
42+
"get_ipython().events.register(\"pre_run_cell\", set_css)"
3843
]
3944
},
4045
{
@@ -66,9 +71,9 @@
6671
"source": [
6772
"## Import Mellea and Specify Requirements\n",
6873
"We create 3 requirements:\n",
69-
"- 1st requirement (r1) will be validated by LLM-as-a-judge on the output of the instruction. This is the default behavior.\n",
70-
"- 2nd requirement (r2) uses a function that takes the output of a sampling step and returns a boolean value indicating successful or unsuccessful validation. While the validation_fn parameter requires to run validation on the full session context, Mellea provides a wrapper for simpler validation functions (simple_validate(fn: Callable[[str], bool])) that take the output string and return a boolean as seen in this case.\n",
71-
"- 3rd requirement is a check(). Checks are only used for validation, not for generation. Checks aim to avoid the \"do not think about B\" effect that often primes models (and humans) to do the opposite and \"think\" about B."
74+
"- First requirement (r1) will be validated by LLM-as-a-judge on the output of the instruction. This is the default behavior.\n",
75+
"- Second requirement (r2) uses a function that takes the output of a sampling step and returns a boolean value indicating successful or unsuccessful validation. While the validation_fn parameter requires to run validation on the full session context, Mellea provides a wrapper for simpler validation functions (simple_validate(fn: Callable[[str], bool])) that take the output string and return a boolean as seen in this case.\n",
76+
"- Third requirement is a check(). Checks are only used for validation, not for generation. Checks aim to avoid the \"do not think about B\" effect that often primes models (and humans) to do the opposite and \"think\" about B."
7277
]
7378
},
7479
{
@@ -79,9 +84,10 @@
7984
},
8085
"outputs": [],
8186
"source": [
87+
"import mellea\n",
8288
"from mellea.stdlib.requirement import check, req, simple_validate\n",
83-
"import mellea # noqa: E402\n",
84-
"from mellea.stdlib.sampling import RejectionSamplingStrategy # noqa: E402\n",
89+
"from mellea.stdlib.sampling import RejectionSamplingStrategy\n",
90+
"\n",
8591
"requirements = [\n",
8692
" req(\"The email should have a salutation\"), # == r1\n",
8793
" req(\n",

0 commit comments

Comments
 (0)