Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
39 changes: 39 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Verify Code Quality

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true

jobs:
quality:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12'] # Need to add 3.13 once we resolve outlines issues.
steps:
- uses: actions/checkout@v4
- name: Install uv and set the python version
uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
- name: pre-commit cache key
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> "$GITHUB_ENV"
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install dependencies
run: uv sync --frozen --all-extras
- name: Check style and run tests
run: pre-commit run --all-files
- name: Send failure message
if: failure() # This step will only run if a previous step failed
run: echo "The quality verification failed. Please run precommit "
2 changes: 1 addition & 1 deletion docs/dev/constrained_decoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The `m` framework currently uses the `format` argument to pydantic schemas, **ou

> 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.

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.
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.

## Integration with grammar-targeted LLMs

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@
"!nohup ollama serve >/dev/null 2>&1 &\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down Expand Up @@ -76,15 +81,18 @@
"source": [
"from mellea import generative\n",
"\n",
"\n",
"# The Summarizer Library\n",
"@generative\n",
"def summarize_meeting(transcript: str) -> str:\n",
" \"\"\"Summarize the meeting transcript into a concise paragraph of main points.\"\"\"\n",
"\n",
"\n",
"@generative\n",
"def summarize_contract(contract_text: str) -> str:\n",
" \"\"\"Produce a natural language summary of contract obligations and risks.\"\"\"\n",
"\n",
"\n",
"@generative\n",
"def summarize_short_story(story: str) -> str:\n",
" \"\"\"Summarize a short story, with one paragraph on plot and one paragraph on braod themes.\"\"\""
Expand All @@ -109,10 +117,12 @@
"def propose_business_decision(summary: str) -> str:\n",
" \"\"\"Given a structured summary with clear recommendations, propose a business decision.\"\"\"\n",
"\n",
"\n",
"@generative\n",
"def generate_risk_mitigation(summary: str) -> str:\n",
" \"\"\"If the summary contains risk elements, propose mitigation strategies.\"\"\"\n",
"\n",
"\n",
"@generative\n",
"def generate_novel_recommendations(summary: str) -> str:\n",
" \"\"\"Provide a list of novel recommendations that are similar in plot or theme to the short story summary.\"\"\""
Expand All @@ -135,16 +145,19 @@
"outputs": [],
"source": [
"# Compose the libraries.\n",
"from typing import Literal # noqa: E402\n",
"from typing import Literal\n",
"\n",
"\n",
"@generative\n",
"def has_structured_conclusion(summary: str) -> Literal[\"yes\", \"no\"]:\n",
" \"\"\"Determine whether the summary contains a clearly marked conclusion or recommendation.\"\"\"\n",
"\n",
"\n",
"@generative\n",
"def contains_actionable_risks(summary: str) -> Literal[\"yes\", \"no\"]:\n",
" \"\"\"Check whether the summary contains references to business risks or exposure.\"\"\"\n",
"\n",
"\n",
"@generative\n",
"def has_theme_and_plot(summary: str) -> Literal[\"yes\", \"no\"]:\n",
" \"\"\"Check whether the summary contains both a plot and thematic elements.\"\"\""
Expand All @@ -166,7 +179,7 @@
},
"outputs": [],
"source": [
"from mellea import start_session # noqa: E402\n",
"from mellea import start_session\n",
"\n",
"m = start_session()"
]
Expand Down
10 changes: 8 additions & 2 deletions docs/examples/notebooks/context_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@
"!nohup ollama serve >/dev/null 2>&1 &\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down Expand Up @@ -80,6 +85,7 @@
"outputs": [],
"source": [
"from mellea import LinearContext, start_session\n",
"\n",
"m = start_session(ctx=LinearContext())\n",
"m.chat(\"Make up a math problem.\")\n",
"m.chat(\"Solve your math problem.\")\n",
Expand Down
18 changes: 13 additions & 5 deletions docs/examples/notebooks/document_mobject.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@
"!nohup ollama serve >/dev/null 2>&1 &\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down Expand Up @@ -77,6 +82,7 @@
"outputs": [],
"source": [
"from mellea.stdlib.docs.richdocument import RichDocument\n",
"\n",
"rd = RichDocument.from_document_file(\"https://arxiv.org/pdf/1906.04043\")"
]
},
Expand All @@ -96,7 +102,8 @@
},
"outputs": [],
"source": [
"from mellea.stdlib.docs.richdocument import Table # noqa: E402\n",
"from mellea.stdlib.docs.richdocument import Table\n",
"\n",
"table1: Table = rd.get_tables()[0]\n",
"print(table1.to_markdown())"
]
Expand All @@ -119,8 +126,9 @@
},
"outputs": [],
"source": [
"from mellea import start_session # noqa: E402\n",
"from mellea.backends.types import ModelOption # noqa: E402\n",
"from mellea import start_session\n",
"from mellea.backends.types import ModelOption\n",
"\n",
"m = start_session()\n",
"for seed in [x * 12 for x in range(5)]:\n",
" table2 = m.transform(\n",
Expand Down
10 changes: 8 additions & 2 deletions docs/examples/notebooks/example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@
"!nohup ollama serve >/dev/null 2>&1 &\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down Expand Up @@ -77,6 +82,7 @@
"outputs": [],
"source": [
"import mellea\n",
"\n",
"m = mellea.start_session()"
]
},
Expand Down
20 changes: 13 additions & 7 deletions docs/examples/notebooks/instruct_validate_repair.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@
"!nohup ollama serve >/dev/null 2>&1 &\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down Expand Up @@ -66,9 +71,9 @@
"source": [
"## Import Mellea and Specify Requirements\n",
"We create 3 requirements:\n",
"- 1st requirement (r1) will be validated by LLM-as-a-judge on the output of the instruction. This is the default behavior.\n",
"- 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",
"- 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."
"- First requirement (r1) will be validated by LLM-as-a-judge on the output of the instruction. This is the default behavior.\n",
"- 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",
"- 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."
]
},
{
Expand All @@ -79,9 +84,10 @@
},
"outputs": [],
"source": [
"import mellea\n",
"from mellea.stdlib.requirement import check, req, simple_validate\n",
"import mellea # noqa: E402\n",
"from mellea.stdlib.sampling import RejectionSamplingStrategy # noqa: E402\n",
"from mellea.stdlib.sampling import RejectionSamplingStrategy\n",
"\n",
"requirements = [\n",
" req(\"The email should have a salutation\"), # == r1\n",
" req(\n",
Expand Down
9 changes: 7 additions & 2 deletions docs/examples/notebooks/m_serve_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@
"!nohup ollama serve >/dev/null 2>&1 &\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down
14 changes: 11 additions & 3 deletions docs/examples/notebooks/mcp_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@
"!uv pip install mcp -q\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down Expand Up @@ -76,6 +81,7 @@
"outputs": [],
"source": [
"from mcp.server.fastmcp import FastMCP\n",
"\n",
"from mellea import MelleaSession\n",
"from mellea.backends import model_ids\n",
"from mellea.backends.ollama import OllamaModelBackend\n",
Expand All @@ -100,6 +106,7 @@
"source": [
"mcp = FastMCP(\"Demo\")\n",
"\n",
"\n",
"@mcp.tool()\n",
"def write_a_poem(word_limit: int) -> str:\n",
" \"\"\"Write a poem with a word limit.\"\"\"\n",
Expand All @@ -116,10 +123,11 @@
" assert isinstance(res, ModelOutputThunk)\n",
" return str(res.value)\n",
"\n",
"\n",
"@mcp.resource(\"greeting://{name}\")\n",
"def get_greeting(name: str) -> str:\n",
" \"\"\"Get a personalized greeting\"\"\"\n",
" return f\"Hello, {name}!\" "
" return f\"Hello, {name}!\""
]
}
],
Expand Down
11 changes: 8 additions & 3 deletions docs/examples/notebooks/model_options_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"source": [
"# Model Options Example\n",
"This Jupyter notebook runs on Colab and demostrates specifying model options"
"This Jupyter notebook runs on Colab and demonstrates specifying model options"
]
},
{
Expand All @@ -33,8 +33,13 @@
"!nohup ollama serve >/dev/null 2>&1 &\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down
12 changes: 10 additions & 2 deletions docs/examples/notebooks/sentiment_classifier.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@
"!nohup ollama serve >/dev/null 2>&1 &\n",
"\n",
"from IPython.display import HTML, display\n",
"def set_css(): display(HTML('\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n'))\n",
"get_ipython().events.register('pre_run_cell',set_css)"
"\n",
"\n",
"def set_css():\n",
" display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
"\n",
"\n",
"get_ipython().events.register(\"pre_run_cell\", set_css)"
]
},
{
Expand Down Expand Up @@ -77,7 +82,10 @@
"outputs": [],
"source": [
"from typing import Literal\n",
"\n",
"from mellea import generative, start_session\n",
"\n",
"\n",
"@generative\n",
"def classify_sentiment(text: str) -> Literal[\"positive\", \"negative\"]:\n",
" \"\"\"Classify the sentiment of the input text as 'positive' or 'negative'.\"\"\""
Expand Down
Loading