From 9f32f21bc87b2b1c62b35d3ad7dc3db4f2bf286b Mon Sep 17 00:00:00 2001 From: Nigel Jones Date: Tue, 26 May 2026 11:50:54 +0100 Subject: [PATCH 1/2] docs(api): normalize source docstring backticks Replace RST-style ``symbol`` notation with Markdown-style `symbol` notation in Python string literals under mellea/ and cli/. Keeps fenced code blocks (triple backticks) intact. Fresh branch off main, superseding PR #659. Fixes #657. Assisted-by: Claude Code Signed-off-by: Nigel Jones --- cli/alora/commands.py | 44 +++--- cli/alora/intrinsic_uploader.py | 36 ++--- cli/alora/readme_generator.py | 28 ++-- cli/alora/train.py | 70 ++++----- cli/alora/upload.py | 8 +- cli/decompose/__init__.py | 6 +- cli/decompose/decompose.py | 30 ++-- cli/decompose/logging.py | 2 +- cli/decompose/pipeline.py | 52 +++--- cli/decompose/utils.py | 4 +- cli/eval/__init__.py | 2 +- cli/eval/commands.py | 16 +- cli/eval/runner.py | 78 ++++----- cli/fix/async_fixer.py | 18 +-- cli/fix/commands.py | 20 +-- cli/fix/genstub_fixer.py | 18 +-- cli/m.py | 20 +-- cli/serve/commands.py | 18 +-- cli/serve/models.py | 2 +- cli/serve/schema_converter.py | 48 +++--- cli/serve/streaming.py | 6 +- mellea/backends/__init__.py | 8 +- mellea/backends/backend.py | 12 +- mellea/backends/bedrock.py | 18 +-- mellea/backends/cache.py | 14 +- mellea/backends/dummy.py | 22 +-- mellea/backends/huggingface.py | 88 +++++------ mellea/backends/kv_block_helpers.py | 20 +-- mellea/backends/litellm.py | 28 ++-- mellea/backends/model_ids.py | 16 +- mellea/backends/model_options.py | 24 +-- mellea/backends/ollama.py | 28 ++-- mellea/backends/openai.py | 50 +++--- mellea/backends/tools.py | 88 +++++------ mellea/backends/utils.py | 16 +- mellea/backends/watsonx.py | 36 ++--- mellea/core/__init__.py | 8 +- mellea/core/backend.py | 46 +++--- mellea/core/base.py | 126 +++++++-------- mellea/core/formatter.py | 14 +- mellea/core/requirement.py | 110 ++++++------- mellea/core/sampling.py | 18 +-- mellea/formatters/__init__.py | 10 +- mellea/formatters/chat_formatter.py | 18 +-- mellea/formatters/granite/base/io.py | 20 +-- mellea/formatters/granite/base/optional.py | 12 +- mellea/formatters/granite/base/types.py | 44 +++--- mellea/formatters/granite/base/util.py | 34 ++-- .../granite/granite3/granite32/input.py | 10 +- .../granite/granite3/granite32/output.py | 6 +- .../granite/granite3/granite33/input.py | 10 +- .../granite/granite3/granite33/output.py | 6 +- mellea/formatters/granite/granite3/input.py | 2 +- mellea/formatters/granite/granite3/output.py | 14 +- mellea/formatters/granite/granite3/types.py | 34 ++-- .../granite/intrinsics/constants.py | 2 +- mellea/formatters/granite/intrinsics/input.py | 46 +++--- .../granite/intrinsics/json_util.py | 66 ++++---- .../formatters/granite/intrinsics/output.py | 86 +++++----- mellea/formatters/granite/intrinsics/util.py | 32 ++-- .../granite/retrievers/elasticsearch.py | 14 +- .../granite/retrievers/embeddings.py | 14 +- mellea/formatters/granite/retrievers/util.py | 20 +-- mellea/formatters/template_formatter.py | 22 +-- mellea/helpers/__init__.py | 8 +- mellea/helpers/async_helpers.py | 16 +- mellea/helpers/openai_compatible_helpers.py | 54 +++---- mellea/helpers/server_type.py | 10 +- mellea/plugins/base.py | 32 ++-- mellea/plugins/context.py | 8 +- mellea/plugins/decorators.py | 14 +- mellea/plugins/hooks/component.py | 34 ++-- mellea/plugins/hooks/generation.py | 32 ++-- mellea/plugins/hooks/sampling.py | 56 +++---- mellea/plugins/hooks/session.py | 20 +-- mellea/plugins/hooks/tool.py | 22 +-- mellea/plugins/hooks/validation.py | 16 +- mellea/plugins/manager.py | 28 ++-- mellea/plugins/pluginset.py | 18 +-- mellea/plugins/policies.py | 8 +- mellea/plugins/registry.py | 76 ++++----- mellea/stdlib/__init__.py | 16 +- mellea/stdlib/chunking.py | 40 ++--- mellea/stdlib/components/chat.py | 56 +++---- mellea/stdlib/components/docs/document.py | 14 +- mellea/stdlib/components/docs/richdocument.py | 66 ++++---- mellea/stdlib/components/genslot.py | 4 +- mellea/stdlib/components/genstub.py | 34 ++-- mellea/stdlib/components/instruction.py | 12 +- mellea/stdlib/components/intrinsic/_util.py | 16 +- mellea/stdlib/components/intrinsic/core.py | 32 ++-- .../stdlib/components/intrinsic/guardian.py | 40 ++--- .../stdlib/components/intrinsic/intrinsic.py | 16 +- mellea/stdlib/components/intrinsic/rag.py | 68 ++++---- mellea/stdlib/components/mify.py | 44 +++--- mellea/stdlib/components/mobject.py | 56 +++---- mellea/stdlib/components/react.py | 14 +- mellea/stdlib/components/simple.py | 26 +-- mellea/stdlib/components/unit_test_eval.py | 20 +-- mellea/stdlib/context.py | 24 +-- mellea/stdlib/frameworks/react.py | 8 +- mellea/stdlib/functional.py | 32 ++-- mellea/stdlib/requirements/md.py | 2 +- mellea/stdlib/requirements/python_reqs.py | 16 +- mellea/stdlib/requirements/requirement.py | 44 +++--- mellea/stdlib/requirements/safety/guardian.py | 28 ++-- mellea/stdlib/requirements/tool_reqs.py | 12 +- mellea/stdlib/sampling/base.py | 2 +- mellea/stdlib/sampling/budget_forcing.py | 16 +- mellea/stdlib/sampling/majority_voting.py | 52 +++--- .../sampling_algos/budget_forcing_alg.py | 28 ++-- mellea/stdlib/sampling/sofai.py | 6 +- mellea/stdlib/session.py | 86 +++++----- mellea/stdlib/start_backend.py | 42 ++--- mellea/stdlib/streaming.py | 148 +++++++++--------- mellea/stdlib/tools/interpreter.py | 42 ++--- mellea/stdlib/tools/mcp.py | 58 +++---- mellea/telemetry/backend_instrumentation.py | 18 +-- mellea/telemetry/context.py | 44 +++--- mellea/telemetry/metrics.py | 24 +-- mellea/telemetry/metrics_plugins.py | 10 +- mellea/telemetry/pricing.py | 38 ++--- mellea/telemetry/tracing.py | 14 +- 123 files changed, 1829 insertions(+), 1829 deletions(-) diff --git a/cli/alora/commands.py b/cli/alora/commands.py index 415589259..96622c5d6 100644 --- a/cli/alora/commands.py +++ b/cli/alora/commands.py @@ -1,10 +1,10 @@ -"""Typer sub-application for the ``m alora`` command group. +"""Typer sub-application for the `m alora` command group. -Provides three commands: ``train`` (fine-tune a base causal language model on a JSONL -dataset to produce a LoRA or aLoRA adapter), ``upload`` (push adapter weights to +Provides three commands: `train` (fine-tune a base causal language model on a JSONL +dataset to produce a LoRA or aLoRA adapter), `upload` (push adapter weights to Hugging Face Hub, optionally packaging the adapter as an intrinsic with an -``io.yaml`` configuration), and ``add-readme`` (use an LLM to auto-generate and -upload an ``INTRINSIC_README.md`` for the trained adapter). +`io.yaml` configuration), and `add-readme` (use an LLM to auto-generate and +upload an `INTRINSIC_README.md` for the trained adapter). """ import json @@ -37,12 +37,12 @@ def alora_train( pairs. Supports both aLoRA (asymmetric LoRA) and standard LoRA adapters. Prerequisites: - Mellea installed with adapter extras (``uv add mellea[adapters]``). + Mellea installed with adapter extras (`uv add mellea[adapters]`). A CUDA, MPS, or CPU device available for training. Output: - Saves adapter weights to the path specified by ``--outfile``. The output - directory contains an ``adapter_config.json`` and the trained weight + Saves adapter weights to the path specified by `--outfile`. The output + directory contains an `adapter_config.json` and the trained weight files, ready for upload or local inference. Examples: @@ -56,8 +56,8 @@ def alora_train( basemodel: Base model ID or path. outfile: Path to save adapter weights. promptfile: Path to load the prompt format file. - adapter: Adapter type; ``"alora"`` or ``"lora"``. - device: Device to train on: ``"auto"``, ``"cpu"``, ``"cuda"``, or ``"mps"``. + adapter: Adapter type; `"alora"` or `"lora"`. + device: Device to train on: `"auto"`, `"cpu"`, `"cuda"`, or `"mps"`. epochs: Number of training epochs. learning_rate: Learning rate for the optimizer. batch_size: Per-device training batch size. @@ -100,14 +100,14 @@ def alora_upload( """Upload a trained adapter to a remote model registry. Pushes adapter weights to Hugging Face Hub, optionally packaging the adapter - as an intrinsic with an ``io.yaml`` configuration file. + as an intrinsic with an `io.yaml` configuration file. Prerequisites: - Hugging Face CLI authenticated (``huggingface-cli login``). + Hugging Face CLI authenticated (`huggingface-cli login`). Output: Creates or updates a Hugging Face Hub repository at the name specified - by ``--name`` and uploads the adapter weight files. + by `--name` and uploads the adapter weight files. Examples: m alora upload ./adapter --name acme/my-alora @@ -118,10 +118,10 @@ def alora_upload( Args: weight_path: Path to saved adapter weights directory. name: Destination model name on Hugging Face Hub - (e.g. ``"acme/carbchecker-alora"``). - intrinsic: If ``True``, the adapter implements an intrinsic and an - ``io.yaml`` file must also be provided. - io_yaml: Path to the ``io.yaml`` file configuring input/output processing + (e.g. `"acme/carbchecker-alora"`). + intrinsic: If `True`, the adapter implements an intrinsic and an + `io.yaml` file must also be provided. + io_yaml: Path to the `io.yaml` file configuring input/output processing when the model is invoked as an intrinsic. """ from cli.alora.intrinsic_uploader import upload_intrinsic @@ -179,12 +179,12 @@ def alora_add_readme( Face Hub repository. Prerequisites: - Hugging Face CLI authenticated (``huggingface-cli login``). + Hugging Face CLI authenticated (`huggingface-cli login`). An LLM backend available for README generation. Output: Generates a README.md file, displays it for confirmation, and uploads - it to the Hugging Face Hub repository specified by ``--name``. + it to the Hugging Face Hub repository specified by `--name`. Examples: m alora add-readme data.jsonl --basemodel ibm-granite/granite-3.3-2b-instruct --name acme/my-alora @@ -195,10 +195,10 @@ def alora_add_readme( Args: datafile: JSONL file with item/label pairs used to train the adapter. basemodel: Base model ID or path. - promptfile: Path to the prompt format file, or ``None``. + promptfile: Path to the prompt format file, or `None`. name: Destination model name on Hugging Face Hub. - hints: Path to a file containing additional domain hints, or ``None``. - io_yaml: Path to the ``io.yaml`` intrinsic configuration file, or ``None``. + hints: Path to a file containing additional domain hints, or `None`. + io_yaml: Path to the `io.yaml` intrinsic configuration file, or `None`. Raises: OSError: If no Hugging Face authentication token is found. diff --git a/cli/alora/intrinsic_uploader.py b/cli/alora/intrinsic_uploader.py index 7b696936f..5a80ae619 100644 --- a/cli/alora/intrinsic_uploader.py +++ b/cli/alora/intrinsic_uploader.py @@ -1,10 +1,10 @@ """Upload a trained adapter to Hugging Face Hub in the intrinsic directory layout. Creates or updates a private Hugging Face repository and uploads adapter weights -into a ``//`` sub-directory, together with -the required ``io.yaml`` configuration file. If an ``INTRINSIC_README.md`` exists in -the weight directory it is also uploaded as the repository's root ``README.md``. -Requires an authenticated Hugging Face token obtained via ``huggingface-cli login``. +into a `//` sub-directory, together with +the required `io.yaml` configuration file. If an `INTRINSIC_README.md` exists in +the weight directory it is also uploaded as the repository's root `README.md`. +Requires an authenticated Hugging Face token obtained via `huggingface-cli login`. """ import os @@ -38,31 +38,31 @@ def upload_intrinsic( """Upload an adapter to Hugging Face Hub using the intrinsic directory layout. Creates or updates a private Hugging Face repository and uploads adapter - weights into a ``//`` sub-directory, - together with the ``io.yaml`` configuration file. If an - ``INTRINSIC_README.md`` exists in the weight directory it is also uploaded - as the repository root ``README.md``. + weights into a `//` sub-directory, + together with the `io.yaml` configuration file. If an + `INTRINSIC_README.md` exists in the weight directory it is also uploaded + as the repository root `README.md`. Args: weight_path (str): Local directory containing the adapter weights - (output of ``save_pretrained``). + (output of `save_pretrained`). model_name (str): Target Hugging Face repository name in - ``"/"`` format (e.g. ``"acme/carbchecker-alora"``). + `"/"` format (e.g. `"acme/carbchecker-alora"`). base_model (str): Base model ID or path (e.g. - ``"ibm-granite/granite-3.3-2b-instruct"``). Must contain at most - one ``"/"`` separator. + `"ibm-granite/granite-3.3-2b-instruct"`). Must contain at most + one `"/"` separator. type (Literal['lora', 'alora']): Adapter type, used as the leaf directory name in the repository layout. - io_yaml (str): Path to the ``io.yaml`` configuration file for + io_yaml (str): Path to the `io.yaml` configuration file for intrinsic input/output processing. private (bool): Whether the repository should be private. Currently - only ``True`` is supported. + only `True` is supported. Raises: - AssertionError: If ``weight_path`` or ``io_yaml`` do not exist, if - ``private`` is ``False``, if ``base_model`` contains more than one - ``"/"`` separator, or if ``model_name`` does not contain exactly - one ``"/"`` separator. + AssertionError: If `weight_path` or `io_yaml` do not exist, if + `private` is `False`, if `base_model` contains more than one + `"/"` separator, or if `model_name` does not contain exactly + one `"/"` separator. OSError: If no Hugging Face authentication token is found. """ try: diff --git a/cli/alora/readme_generator.py b/cli/alora/readme_generator.py index de2643e20..bda96987a 100644 --- a/cli/alora/readme_generator.py +++ b/cli/alora/readme_generator.py @@ -1,10 +1,10 @@ """LLM-assisted generator for adapter intrinsic README files. -Uses a ``MelleaSession`` with rejection sampling to derive README template variables +Uses a `MelleaSession` with rejection sampling to derive README template variables from a JSONL training dataset — including a high-level description, the inferred Python argument list, and Jinja2-renderable sample rows. Validates the generated output with deterministic requirements (correct naming conventions, syntactically -valid argument lists) before rendering the final ``INTRINSIC_README.md`` via a +valid argument lists) before rendering the final `INTRINSIC_README.md` via a Jinja2 template. """ @@ -28,10 +28,10 @@ class ReadmeTemplateVars(BaseModel): high_level_description (str): A 2-3 sentence description of what the intrinsic adapter does. dataset_description (str): Brief description of the training dataset contents and format. userid (str): HuggingFace user ID (the namespace portion of the model name). - intrinsic_name (str): Short snake_case identifier for the intrinsic (e.g. ``"carbchecker"``). - intrinsic_name_camelcase (str): CamelCase version of ``intrinsic_name`` (e.g. ``"CarbChecker"``). - arglist (str): Python function argument list with type hints (e.g. ``"description: str"``). - arglist_without_type_annotations (str): Argument list without type hints (e.g. ``"description"``). + intrinsic_name (str): Short snake_case identifier for the intrinsic (e.g. `"carbchecker"`). + intrinsic_name_camelcase (str): CamelCase version of `intrinsic_name` (e.g. `"CarbChecker"`). + arglist (str): Python function argument list with type hints (e.g. `"description: str"`). + arglist_without_type_annotations (str): Argument list without type hints (e.g. `"description"`). """ high_level_description: str @@ -141,20 +141,20 @@ def make_readme_jinja_dict( """Generate all template variables for the intrinsic README using an LLM. Loads the first five lines of the JSONL dataset, determines the input structure, - and uses ``m.instruct`` with deterministic requirements and rejection sampling to + and uses `m.instruct` with deterministic requirements and rejection sampling to generate README template variables. Args: - m: Active ``MelleaSession`` to use for LLM generation. + m: Active `MelleaSession` to use for LLM generation. dataset_path: Path to the JSONL training dataset file. base_model: Base model ID or path used to train the adapter. prompt_file: Path to the prompt format file (empty string if not provided). name: Destination model name on Hugging Face Hub - (e.g. ``"acme/carbchecker-alora"``). + (e.g. `"acme/carbchecker-alora"`). hints: Optional string of additional domain hints to include in the prompt. Returns: - Dict of Jinja2 template variables for rendering the ``INTRINSIC_README.md``. + Dict of Jinja2 template variables for rendering the `INTRINSIC_README.md`. """ # Load first 5 lines of the dataset. samples = [] @@ -294,19 +294,19 @@ def generate_readme( ) -> str: """Generate an INTRINSIC_README.md file from the dataset and template. - Creates a ``MelleaSession``, uses the LLM to generate template variables, - renders the Jinja template, and writes the result to ``output_path``. + Creates a `MelleaSession`, uses the LLM to generate template variables, + renders the Jinja template, and writes the result to `output_path`. Args: dataset_path: Path to the JSONL training dataset file. base_model: Base model ID or path used to train the adapter. - prompt_file: Path to the prompt format file, or ``None``. + prompt_file: Path to the prompt format file, or `None`. output_path: Destination path for the generated README file. name: Destination model name on Hugging Face Hub. hints: Optional string of additional domain hints for the LLM. Returns: - The path to the written output file (same as ``output_path``). + The path to the written output file (same as `output_path`). """ from jinja2 import Environment, FileSystemLoader diff --git a/cli/alora/train.py b/cli/alora/train.py index 328ed35d9..722a20096 100644 --- a/cli/alora/train.py +++ b/cli/alora/train.py @@ -1,10 +1,10 @@ """Fine-tune a causal language model to produce a LoRA or aLoRA adapter. -Loads a JSONL dataset of ``item``/``label`` pairs, applies an 80/20 train/validation -split, and trains using HuggingFace PEFT and TRL's ``SFTTrainer`` — saving the +Loads a JSONL dataset of `item`/`label` pairs, applies an 80/20 train/validation +split, and trains using HuggingFace PEFT and TRL's `SFTTrainer` — saving the checkpoint with the lowest validation loss. Supports CUDA, MPS (macOS, PyTorch ≥ 2.8), and CPU device selection, and handles the -``alora_invocation_tokens`` configuration required for aLoRA training. +`alora_invocation_tokens` configuration required for aLoRA training. """ import json @@ -57,18 +57,18 @@ def load_dataset_from_json( ) -> Dataset: """Load a JSONL dataset and format it for SFT training. - Reads ``item``/``label`` pairs from a JSONL file and builds a HuggingFace - ``Dataset`` with ``input`` and ``target`` columns. Each input is formatted as - ``"{item}\\nRequirement: <|end_of_text|>\\n{invocation_prompt}"``. + Reads `item`/`label` pairs from a JSONL file and builds a HuggingFace + `Dataset` with `input` and `target` columns. Each input is formatted as + `"{item}\\nRequirement: <|end_of_text|>\\n{invocation_prompt}"`. Args: - json_path: Path to the JSONL file containing ``item``/``label`` pairs. + json_path: Path to the JSONL file containing `item`/`label` pairs. tokenizer: HuggingFace tokenizer instance (currently unused, reserved for future tokenization steps). invocation_prompt: Invocation string appended to each input prompt. Returns: - A HuggingFace ``Dataset`` with ``"input"`` and ``"target"`` string columns. + A HuggingFace `Dataset` with `"input"` and `"target"` string columns. """ data = [] with open(json_path, encoding="utf-8") as f: @@ -92,12 +92,12 @@ def formatting_prompts_func(example: dict) -> list[str]: """Concatenate input and target columns for SFT prompt formatting. Args: - example: A batch dict with ``"input"`` and ``"target"`` list fields, as - produced by HuggingFace ``Dataset.map`` in batched mode. + example: A batch dict with `"input"` and `"target"` list fields, as + produced by HuggingFace `Dataset.map` in batched mode. Returns: - A list of strings, each formed by concatenating the ``input`` and - ``target`` values for a single example in the batch. + A list of strings, each formed by concatenating the `input` and + `target` values for a single example in the batch. """ return [ f"{example['input'][i]}{example['target'][i]}" @@ -110,7 +110,7 @@ class SaveBestModelCallback(TrainerCallback): Attributes: best_eval_loss (float): Lowest evaluation loss seen so far across all - evaluation steps. Initialised to ``float("inf")``. + evaluation steps. Initialised to `float("inf")`. """ def __init__(self): @@ -126,18 +126,18 @@ def on_evaluate( """Save the adapter weights if the current evaluation loss is a new best. Called automatically by the HuggingFace Trainer after each evaluation - step. Compares the current ``eval_loss`` from ``metrics`` against - ``best_eval_loss`` and, if lower, updates the stored best and saves the - model to ``args.output_dir``. + step. Compares the current `eval_loss` from `metrics` against + `best_eval_loss` and, if lower, updates the stored best and saves the + model to `args.output_dir`. Args: - args: ``TrainingArguments`` instance with training configuration, - including ``output_dir``. - state: ``TrainerState`` instance with the current training state. - control: ``TrainerControl`` instance for controlling training flow. + args: `TrainingArguments` instance with training configuration, + including `output_dir`. + state: `TrainerState` instance with the current training state. + control: `TrainerControl` instance for controlling training flow. **kwargs: Additional keyword arguments provided by the Trainer, - including ``"model"`` (the current PEFT model) and - ``"metrics"`` (a dict containing ``"eval_loss"``). + including `"model"` (the current PEFT model) and + `"metrics"` (a dict containing `"eval_loss"`). """ model = kwargs["model"] metrics = kwargs["metrics"] @@ -153,13 +153,13 @@ class SafeSaveTrainer(SFTTrainer): def save_model(self, output_dir: str | None = None, _internal_call: bool = False): """Save the model and tokenizer with safe serialization always enabled. - Overrides ``SFTTrainer.save_model`` to call ``save_pretrained`` with - ``safe_serialization=True``, ensuring weights are saved in safetensors + Overrides `SFTTrainer.save_model` to call `save_pretrained` with + `safe_serialization=True`, ensuring weights are saved in safetensors format rather than the legacy pickle-based format. Args: output_dir (str | None): Directory to save the model into. If - ``None``, the trainer's configured ``output_dir`` is used. + `None`, the trainer's configured `output_dir` is used. _internal_call (bool): Internal flag passed through from the Trainer base class; not used by this override. """ @@ -186,8 +186,8 @@ def train_model( """Fine-tune a causal language model to produce a LoRA or aLoRA adapter. Loads and 80/20-splits the JSONL dataset, configures PEFT with the specified - adapter type, trains using ``SFTTrainer`` with a best-checkpoint callback, saves - the adapter weights, and removes the PEFT-generated ``README.md`` from the output + adapter type, trains using `SFTTrainer` with a best-checkpoint callback, saves + the adapter weights, and removes the PEFT-generated `README.md` from the output directory. Args: @@ -195,11 +195,11 @@ def train_model( base_model: Hugging Face model ID or local path to the base model. output_file: Destination path for the trained adapter weights. prompt_file: Optional path to a JSON config file with an - ``"invocation_prompt"`` key. Defaults to the aLoRA invocation token. - adapter: Adapter type to train -- ``"alora"`` (default) or ``"lora"``. - device: Device selection -- ``"auto"``, ``"cpu"``, ``"cuda"``, or - ``"mps"``. - run_name: Name of the training run (passed to ``SFTConfig``). + `"invocation_prompt"` key. Defaults to the aLoRA invocation token. + adapter: Adapter type to train -- `"alora"` (default) or `"lora"`. + device: Device selection -- `"auto"`, `"cpu"`, `"cuda"`, or + `"mps"`. + run_name: Name of the training run (passed to `SFTConfig`). epochs: Number of training epochs. learning_rate: Optimizer learning rate. batch_size: Per-device training batch size. @@ -207,10 +207,10 @@ def train_model( grad_accum: Gradient accumulation steps. Raises: - ValueError: If ``device`` is not one of ``"auto"``, ``"cpu"``, - ``"cuda"``, or ``"mps"``. + ValueError: If `device` is not one of `"auto"`, `"cpu"`, + `"cuda"`, or `"mps"`. RuntimeError: If the GPU has insufficient VRAM to load the model - (wraps ``NotImplementedError`` for meta tensor errors). + (wraps `NotImplementedError` for meta tensor errors). """ if prompt_file: # load the configurable variable invocation_prompt diff --git a/cli/alora/upload.py b/cli/alora/upload.py index 9dccf5a55..66b93b95c 100644 --- a/cli/alora/upload.py +++ b/cli/alora/upload.py @@ -1,9 +1,9 @@ """Upload a trained LoRA or aLoRA adapter to Hugging Face Hub. Creates the target repository if it does not already exist and pushes the entire -adapter weights directory (output of ``save_pretrained``) to the repository root. -Requires an authenticated Hugging Face token set via the ``HF_TOKEN`` environment -variable or ``huggingface-cli login``. +adapter weights directory (output of `save_pretrained`) to the repository root. +Requires an authenticated Hugging Face token set via the `HF_TOKEN` environment +variable or `huggingface-cli login`. """ import os @@ -26,7 +26,7 @@ def upload_model(weight_path: str, model_name: str, private: bool = True): private (bool): Whether the repo should be private. Default: True. Raises: - FileNotFoundError: If ``weight_path`` does not exist on disk. + FileNotFoundError: If `weight_path` does not exist on disk. OSError: If no Hugging Face authentication token is found. RuntimeError: If creating or accessing the Hugging Face repository fails. """ diff --git a/cli/decompose/__init__.py b/cli/decompose/__init__.py index 76d37d5bf..f538fbd44 100644 --- a/cli/decompose/__init__.py +++ b/cli/decompose/__init__.py @@ -1,10 +1,10 @@ -"""Typer sub-application for the ``m decompose`` command group. +"""Typer sub-application for the `m decompose` command group. -Exposes a single ``run`` command that takes a task prompt (from a file or +Exposes a single `run` command that takes a task prompt (from a file or interactively), calls the LLM-based decomposition pipeline to break it into structured subtasks with constraints and dependency ordering, and writes the results as a JSON data file and a ready-to-run Python script. Invoke via -``m decompose run --help`` for full option documentation. +`m decompose run --help` for full option documentation. """ import typer diff --git a/cli/decompose/decompose.py b/cli/decompose/decompose.py index d9b46f4ce..6ca1dedbe 100644 --- a/cli/decompose/decompose.py +++ b/cli/decompose/decompose.py @@ -1,5 +1,5 @@ # decompose/decompose.py -"""Implementation of the ``m decompose run`` CLI command. +"""Implementation of the `m decompose run` CLI command. Accepts a task prompt (from a text file or interactive input), calls the multi-step LLM decomposition pipeline to produce a structured list of subtasks each with @@ -30,7 +30,7 @@ class DecompVersion(StrEnum): """Available template versions for generated decomposition programs. Newer concrete versions must be declared after older ones so that - ``latest`` can resolve to the most recently declared template version. + `latest` can resolve to the most recently declared template version. Attributes: latest: Sentinel value that resolves to the last declared concrete @@ -51,11 +51,11 @@ class DecompVersion(StrEnum): def reorder_subtasks( subtasks: list[DecompSubtasksResult], ) -> list[DecompSubtasksResult]: - """Topologically sort subtasks by their ``depends_on`` relationships. + """Topologically sort subtasks by their `depends_on` relationships. Args: - subtasks: List of subtask dicts, each with a ``"tag"`` and optional - ``"depends_on"`` field. + subtasks: List of subtask dicts, each with a `"tag"` and optional + `"depends_on"` field. Returns: list[DecompSubtasksResult]: The subtasks reordered so that dependencies @@ -107,7 +107,7 @@ def verify_user_variables( Args: decomp_data: The decomposition pipeline result containing subtasks. - input_var: User-provided input variable names, or ``None`` for none. + input_var: User-provided input variable names, or `None` for none. Returns: DecompPipelineResult: The (possibly reordered) decomposition data. @@ -249,11 +249,11 @@ def run( output directory. Prerequisites: - Mellea installed (``uv add mellea``). An Ollama instance running locally, - or an OpenAI-compatible endpoint configured via ``--backend-endpoint``. + Mellea installed (`uv add mellea`). An Ollama instance running locally, + or an OpenAI-compatible endpoint configured via `--backend-endpoint`. Output: - Creates a directory ``//`` containing a JSON + Creates a directory `//` containing a JSON decomposition result file, a ready-to-run Python script, and any generated validation modules. One directory per task job. @@ -278,19 +278,19 @@ def run( backend_endpoint: Endpoint URL or base URL required by remote backends. backend_api_key: API key required by remote backends. version: Template version used to render the generated Python program. - ``latest`` resolves to the most recently declared concrete version. + `latest` resolves to the most recently declared concrete version. input_var: Optional user input variable names to expose in generated prompts and programs. Each name must be a valid non-keyword Python identifier. log_mode: Logging verbosity for CLI and pipeline execution. enable_script_run: Whether generated scripts should expose argparse - runtime options. Defaults to ``False``. + runtime options. Defaults to `False`. Raises: - AssertionError: If ``out_name`` is invalid, ``out_dir`` does not name an - existing directory, ``input_file`` does not name an existing file, - or any declared ``input_var`` is not a valid Python identifier. - ValueError: If ``input_file`` exists but contains no non-empty task + AssertionError: If `out_name` is invalid, `out_dir` does not name an + existing directory, `input_file` does not name an existing file, + or any declared `input_var` is not a valid Python identifier. + ValueError: If `input_file` exists but contains no non-empty task lines. Exception: Propagates pipeline, rendering, parsing, or file-writing failures. Any output directories created earlier in the run are diff --git a/cli/decompose/logging.py b/cli/decompose/logging.py index 0e2bda409..ab6c0705f 100644 --- a/cli/decompose/logging.py +++ b/cli/decompose/logging.py @@ -58,7 +58,7 @@ def get_logger(name: str) -> logging.Logger: name: Logger name, typically a module or component identifier. Returns: - A configured ``logging.Logger`` instance. + A configured `logging.Logger` instance. """ return logging.getLogger(name) diff --git a/cli/decompose/pipeline.py b/cli/decompose/pipeline.py index dcecba6c6..e154b3772 100644 --- a/cli/decompose/pipeline.py +++ b/cli/decompose/pipeline.py @@ -1,9 +1,9 @@ # decompose/pipeline.py """Core decomposition pipeline for turning a task query into scheduled subtasks. -Provides the ``decompose()`` function, which orchestrates a series of LLM calls +Provides the `decompose()` function, which orchestrates a series of LLM calls (subtask listing, constraint extraction, validation strategy selection, prompt -generation, and constraint assignment) to produce a ``DecompPipelineResult`` +generation, and constraint assignment) to produce a `DecompPipelineResult` containing subtasks, per-subtask prompts, constraints, and dependency information. Supports Ollama and OpenAI-compatible inference backends. @@ -37,11 +37,11 @@ class ConstraintValData(TypedDict): """Validation metadata associated with a single extracted constraint. Attributes: - val_strategy: Validation mode selected for the constraint. ``"code"`` - means the pipeline generated validation code; ``"llm"`` means the + val_strategy: Validation mode selected for the constraint. `"code"` + means the pipeline generated validation code; `"llm"` means the constraint should be checked by model reasoning rather than code. - val_fn: Generated validation function source code when ``val_strategy`` - is ``"code"``; otherwise ``None``. + val_fn: Generated validation function source code when `val_strategy` + is `"code"`; otherwise `None`. """ val_strategy: Literal["code", "llm"] @@ -55,7 +55,7 @@ class ConstraintResult(TypedDict): constraint: Natural-language description of the constraint. val_strategy: Validation mode assigned to the constraint. val_fn: Generated validation function source code when the constraint - uses code-based validation; otherwise ``None``. + uses code-based validation; otherwise `None`. val_fn_name: Stable function name assigned to the generated validation function in serialized output. """ @@ -76,14 +76,14 @@ class DecompSubtasksResult(TypedDict): constraints: Constraints assigned to this subtask, each with validation metadata. prompt_template: Jinja2 prompt template for this subtask, with - ``{{ variable }}`` placeholders for user inputs and prior subtask + `{{ variable }}` placeholders for user inputs and prior subtask results. general_instructions: Additional general instructions derived from the prompt template for this subtask. input_vars_required: Ordered list of user-provided input variable names - referenced in ``prompt_template``. + referenced in `prompt_template`. depends_on: Ordered list of subtask tags whose results are referenced in - ``prompt_template``. + `prompt_template`. generated_response: Model response produced during execution. This field is absent until the subtask has been run. """ @@ -146,7 +146,7 @@ def _extract_jinja_vars(prompt_template: str) -> list[str]: Args: prompt_template: Prompt template containing zero or more Jinja - expressions such as ``{{ variable }}``. + expressions such as `{{ variable }}`. Returns: A list of variable expressions captured from the template in match @@ -164,8 +164,8 @@ def _preview_text(text: str, max_len: int = 240) -> str: Returns: A single-line preview string. Returns the normalized full text when its - length does not exceed ``max_len``; otherwise returns a truncated - preview ending with ``" ..."``. + length does not exceed `max_len`; otherwise returns a truncated + preview ending with `" ..."`. """ text = " ".join(text.strip().split()) if len(text) <= max_len: @@ -186,8 +186,8 @@ def build_backend_session( ) -> MelleaSession: """Builds a model session for the configured inference backend. - Initializes and returns a ``MelleaSession`` backed by Ollama - or an OpenAI-compatible endpoint, depending on ``backend``. + Initializes and returns a `MelleaSession` backed by Ollama + or an OpenAI-compatible endpoint, depending on `backend`. Args: model_id: Model identifier passed to the selected backend. @@ -198,10 +198,10 @@ def build_backend_session( log_mode: Logging verbosity for pipeline execution. Returns: - A configured ``MelleaSession`` ready for prompt generation calls. + A configured `MelleaSession` ready for prompt generation calls. Raises: - AssertionError: If ``backend`` is ``DecompBackend.openai`` and + AssertionError: If `backend` is `DecompBackend.openai` and the required endpoint or API key is not provided. """ logger = get_logger("m_decompose.backend") @@ -263,8 +263,8 @@ def task_decompose( log_mode: Logging verbosity for this stage. Returns: - A tuple ``(subtasks, task_constraints)``, where ``subtasks`` is the - ordered list of parsed ``SubtaskItem`` objects and ``task_constraints`` + A tuple `(subtasks, task_constraints)`, where `subtasks` is the + ordered list of parsed `SubtaskItem` objects and `task_constraints` is the ordered list of extracted natural-language constraint strings. """ logger = get_logger("m_decompose.task_decompose") @@ -300,7 +300,7 @@ def constraint_validate( """Selects a validation mode for each constraint and generates code when needed. For every extracted constraint, this stage chooses a validation strategy. - When the selected strategy is ``"code"``, it also generates a validation + When the selected strategy is `"code"`, it also generates a validation function body for later serialization or execution. Args: @@ -367,13 +367,13 @@ def task_execute( log_mode: Logging verbosity for this stage. Returns: - A list of ``SubtaskPromptConstraintsItem`` objects containing each + A list of `SubtaskPromptConstraintsItem` objects containing each subtask, its generated prompt template, and the constraints assigned to that subtask. Raises: Exception: Re-raises the last exception produced by - ``subtask_constraint_assign`` after all retry attempts fail. + `subtask_constraint_assign` after all retry attempts fail. RuntimeError: If constraint assignment fails without preserving a final exception object. """ @@ -503,7 +503,7 @@ def finalize_result( This stage resolves Jinja dependencies for each subtask, attaches validation metadata to assigned constraints, generates general instructions from each - prompt template, and assembles the final ``DecompPipelineResult``. + prompt template, and assembles the final `DecompPipelineResult`. Args: m_session: Active model session used to generate general instructions. @@ -518,7 +518,7 @@ def finalize_result( log_mode: Logging verbosity for this stage. Returns: - A ``DecompPipelineResult`` containing the original prompt, extracted + A `DecompPipelineResult` containing the original prompt, extracted constraints, and fully annotated subtasks with dependency and validation information. """ @@ -619,7 +619,7 @@ def decompose( Args: task_prompt: Natural-language description of the task to decompose. user_input_variable: Optional list of user input variable names that may - be referenced in generated Jinja prompt templates. Pass ``None`` or + be referenced in generated Jinja prompt templates. Pass `None` or an empty list when the task requires no external input variables. model_id: Model name or identifier used for all pipeline stages. backend: Inference backend used for all model calls. @@ -631,7 +631,7 @@ def decompose( log_mode: Logging verbosity for the pipeline run. Returns: - A ``DecompPipelineResult`` containing the original prompt, extracted + A `DecompPipelineResult` containing the original prompt, extracted subtask list, identified constraints, and fully annotated subtask records. diff --git a/cli/decompose/utils.py b/cli/decompose/utils.py index e170c447a..c5438476d 100644 --- a/cli/decompose/utils.py +++ b/cli/decompose/utils.py @@ -1,6 +1,6 @@ """Filename validation utilities for the decompose pipeline. -Provides ``validate_filename``, which checks that a candidate output filename +Provides `validate_filename`, which checks that a candidate output filename contains only safe characters (alphanumeric, underscores, hyphens, periods, and spaces) and falls within a reasonable length limit. Used to prevent path-traversal or shell-injection issues when writing decomposition output files. @@ -18,7 +18,7 @@ def validate_filename(candidate_str: str) -> bool: candidate_str: The filename candidate to validate. Returns: - ``True`` if the string is a safe, valid filename; ``False`` otherwise. + `True` if the string is a safe, valid filename; `False` otherwise. """ import re diff --git a/cli/eval/__init__.py b/cli/eval/__init__.py index cb57f3230..bad943576 100644 --- a/cli/eval/__init__.py +++ b/cli/eval/__init__.py @@ -1,6 +1,6 @@ """CLI package for test-based LLM evaluation. -Provides the ``m eval`` command group, which orchestrates running a generator model +Provides the `m eval` command group, which orchestrates running a generator model against structured test files and scoring each response with a judge model. Each test file specifies a set of instructions and input examples; results — including per-input pass/fail judgements and cumulative pass rates — are written to JSON or JSONL for diff --git a/cli/eval/commands.py b/cli/eval/commands.py index 8525c6989..bc639a6ad 100644 --- a/cli/eval/commands.py +++ b/cli/eval/commands.py @@ -58,13 +58,13 @@ def eval_run( aggregated results to a file. Prerequisites: - Mellea installed (``uv add mellea``). At least one inference backend + Mellea installed (`uv add mellea`). At least one inference backend available (Ollama by default). A separate judge backend/model is recommended but optional (defaults to the generation backend). Output: - Writes evaluation results to ``.`` (default - ``eval_results.json``). The file contains per-test-case scores, judge + Writes evaluation results to `.` (default + `eval_results.json`). The file contains per-test-case scores, judge verdicts, and aggregate statistics. Examples: @@ -76,15 +76,15 @@ def eval_run( Args: test_files: Paths to JSON/JSONL files containing test cases. backend: Generation backend name. - model: Generation model name, or ``None`` for the default. + model: Generation model name, or `None` for the default. max_gen_tokens: Maximum tokens to generate for each response. - judge_backend: Judge backend name, or ``None`` to reuse the generation + judge_backend: Judge backend name, or `None` to reuse the generation backend. - judge_model: Judge model name, or ``None`` for the default. + judge_model: Judge model name, or `None` for the default. max_judge_tokens: Maximum tokens for the judge model's output. output_path: File path prefix for the results file. - output_format: Output format -- ``"json"`` or ``"jsonl"``. - continue_on_error: If ``True``, skip failed tests instead of raising. + output_format: Output format -- `"json"` or `"jsonl"`. + continue_on_error: If `True`, skip failed tests instead of raising. """ from cli.eval.runner import run_evaluations diff --git a/cli/eval/runner.py b/cli/eval/runner.py index 8bdc6682b..d3da1bc25 100644 --- a/cli/eval/runner.py +++ b/cli/eval/runner.py @@ -1,8 +1,8 @@ """Execution engine for the test-based LLM evaluation pipeline. -Loads JSON test files into ``TestBasedEval`` objects and, for each test, runs a +Loads JSON test files into `TestBasedEval` objects and, for each test, runs a generator model to produce responses and a separate judge model to score them. Parses -the judge output for a ``{"score": ..., "justification": ...}`` JSON fragment, +the judge output for a `{"score": ..., "justification": ...}` JSON fragment, aggregates per-input pass/fail counts, and saves the full results to JSON or JSONL. """ @@ -30,7 +30,7 @@ class InputEvalResult: input_text (str): The raw input text sent to the generation model. model_output (str): The text response produced by the generation model. validation_passed (bool): Whether the judge scored this response as passing. - score (int): Numeric score assigned by the judge (``1`` for pass, ``0`` for fail). + score (int): Numeric score assigned by the judge (`1` for pass, `0` for fail). validation_reason (str): Justification text returned by the judge model. """ @@ -53,8 +53,8 @@ def to_dict(self) -> dict: """Serialise the input evaluation result to a plain dictionary. Returns: - dict: A dictionary with keys ``"input"``, ``"model_output"``, - ``"passed"``, ``"score"``, and ``"justification"``. + dict: A dictionary with keys `"input"`, `"model_output"`, + `"passed"`, `"score"`, and `"justification"`. """ return { "input": self.input_text, @@ -77,7 +77,7 @@ class TestEvalResult: Attributes: passed_count (int): Number of inputs that received a passing score. total_count (int): Total number of inputs evaluated. - pass_rate (float): Fraction of inputs that passed (``passed_count / total_count``). + pass_rate (float): Fraction of inputs that passed (`passed_count / total_count`). """ def __init__(self, test_eval: TestBasedEval, input_results: list[InputEvalResult]): @@ -88,11 +88,11 @@ def to_dict(self) -> dict: """Serialise the test evaluation result to a plain dictionary. Returns: - dict: A dictionary containing the test metadata (``"test_id"``, - ``"source"``, ``"name"``, ``"instructions"``), per-input results - under ``"input_results"``, expected targets under - ``"expected_targets"``, and summary counts (``"passed"``, - ``"total_count"``, ``"pass_rate"``). + dict: A dictionary containing the test metadata (`"test_id"`, + `"source"`, `"name"`, `"instructions"`), per-input results + under `"input_results"`, expected targets under + `"expected_targets"`, and summary counts (`"passed"`, + `"total_count"`, `"pass_rate"`). """ return { "test_id": self.test_eval.test_id, @@ -125,18 +125,18 @@ def create_session( """Create a mellea session with the specified backend and model. Args: - backend: Backend name: ``"ollama"``, ``"openai"``, ``"hf"``, - ``"watsonx"``, or ``"litellm"``. - model: Model ID or ``ModelIdentifier`` attribute name, or ``None`` + backend: Backend name: `"ollama"`, `"openai"`, `"hf"`, + `"watsonx"`, or `"litellm"`. + model: Model ID or `ModelIdentifier` attribute name, or `None` to use the default model. - max_tokens: Maximum number of tokens to generate, or ``None`` for + max_tokens: Maximum number of tokens to generate, or `None` for the backend default. Returns: - A configured ``MelleaSession`` ready for generation. + A configured `MelleaSession` ready for generation. Raises: - ValueError: If ``backend`` is not one of the supported backend names. + ValueError: If `backend` is not one of the supported backend names. Exception: Re-raised from backend or session construction if initialisation fails. """ @@ -230,20 +230,20 @@ def run_evaluations( Args: test_files: List of paths to JSON test files. Each file should contain - ``"id"``, ``"source"``, ``"name"``, ``"instructions"``, and - ``"examples"`` fields. + `"id"`, `"source"`, `"name"`, `"instructions"`, and + `"examples"` fields. backend: Backend name for the generation model. - model: Model ID for the generator, or ``None`` for the default. - max_gen_tokens: Maximum tokens for the generator, or ``None`` for the + model: Model ID for the generator, or `None` for the default. + max_gen_tokens: Maximum tokens for the generator, or `None` for the backend default. - judge_backend: Backend name for the judge model, or ``None`` to reuse + judge_backend: Backend name for the judge model, or `None` to reuse the generation backend. - judge_model: Model ID for the judge, or ``None`` for the default. - max_judge_tokens: Maximum tokens for the judge, or ``None`` for the + judge_model: Model ID for the judge, or `None` for the default. + max_judge_tokens: Maximum tokens for the judge, or `None` for the backend default. output_path: File path prefix for saving results. - output_format: Output format: ``"json"`` or ``"jsonl"``. - continue_on_error: If ``True``, skip failed test evaluations instead of + output_format: Output format: `"json"` or `"jsonl"`. + continue_on_error: If `True`, skip failed test evaluations instead of raising. """ all_test_evals: list[TestBasedEval] = [] @@ -314,16 +314,16 @@ def execute_test_eval( ) -> TestEvalResult: """Execute a single test evaluation. - For each input in the test, generates a response using ``generation_session``, - then validates using ``judge_session``. + For each input in the test, generates a response using `generation_session`, + then validates using `judge_session`. Args: - test_eval: The ``TestBasedEval`` object containing inputs and targets. - generation_session: ``MelleaSession`` used to produce model responses. - judge_session: ``MelleaSession`` used to score model responses. + test_eval: The `TestBasedEval` object containing inputs and targets. + generation_session: `MelleaSession` used to produce model responses. + judge_session: `MelleaSession` used to score model responses. Returns: - A ``TestEvalResult`` with per-input pass/fail outcomes. + A `TestEvalResult` with per-input pass/fail outcomes. """ input_results = [] @@ -367,7 +367,7 @@ def execute_test_eval( def _extract_first_json(text: str) -> dict | None: - """Return the first JSON object containing a ``"score"`` key, or ``None``.""" + """Return the first JSON object containing a `"score"` key, or `None`.""" decoder = json.JSONDecoder() for i, ch in enumerate(text): if ch == "{": @@ -387,8 +387,8 @@ def parse_judge_output(judge_output: str) -> tuple[int | None, str]: judge_output: Raw text output from the judge model. Returns: - A ``(score, justification)`` tuple where ``score`` is an integer (or - ``None`` if parsing failed) and ``justification`` is an explanatory + A `(score, justification)` tuple where `score` is an integer (or + `None` if parsing failed) and `justification` is an explanatory string. """ data = _extract_first_json(judge_output) @@ -412,10 +412,10 @@ def save_results(results: list[TestEvalResult], output_path: str, output_format: """Persist evaluation results to disk in JSON or JSONL format. Args: - results: List of ``TestEvalResult`` objects to serialise. + results: List of `TestEvalResult` objects to serialise. output_path: Destination file path (extension may be appended if it - does not match ``output_format``). - output_format: Format string: ``"json"`` or ``"jsonl"``. + does not match `output_format`). + output_format: Format string: `"json"` or `"jsonl"`. """ output_path_obj = Path(output_path) if output_path_obj.suffix != f".{output_format}": @@ -452,7 +452,7 @@ def summary_stats(results: list[TestEvalResult]): """Print aggregated pass-rate statistics for a set of evaluation results. Args: - results: List of ``TestEvalResult`` objects to summarise. + results: List of `TestEvalResult` objects to summarise. """ total_inputs = sum(r.total_count for r in results) passed_inputs = sum(r.passed_count for r in results) diff --git a/cli/fix/async_fixer.py b/cli/fix/async_fixer.py index 104ce8c17..78ad29ccd 100644 --- a/cli/fix/async_fixer.py +++ b/cli/fix/async_fixer.py @@ -230,9 +230,9 @@ def _has_existing_consumption(following_stmts: list[ast.stmt], var_name: str) -> """Check if subsequent statements already consume/await the thunk variable. Looks for: - - ``while not .is_computed(): ...`` - - ``await .avalue()`` - - ``await .astream()`` + - `while not .is_computed(): ...` + - `await .avalue()` + - `await .astream()` Recurses into nested blocks (if/for/try/with) via ast.walk. """ @@ -287,7 +287,7 @@ def find_fixable_calls(source: str, filepath: Path) -> list[FixLocation]: filepath: Path used for error messages and AST filename metadata. Returns: - List of ``FixLocation`` objects describing each call site that should be fixed. + List of `FixLocation` objects describing each call site that should be fixed. """ if not source.strip(): return [] @@ -453,10 +453,10 @@ def fix_file( Args: filepath: Path to the Python file to fix. mode: Fix strategy to apply. - dry_run: If ``True``, return locations without modifying the file. + dry_run: If `True`, return locations without modifying the file. Returns: - List of ``FixLocation`` objects for each call site found (and optionally fixed). + List of `FixLocation` objects for each call site found (and optionally fixed). """ source = filepath.read_text() locations = find_fixable_calls(source, filepath) @@ -515,12 +515,12 @@ def fix_path(path: Path, mode: _FixMode, dry_run: bool = False) -> FixResult: Args: path: File or directory to process. Directories are scanned recursively for - ``*.py`` files. + `*.py` files. mode: Fix strategy to apply. - dry_run: If ``True``, report locations without modifying files. + dry_run: If `True`, report locations without modifying files. Returns: - Aggregated ``FixResult`` with all fix locations and summary counts. + Aggregated `FixResult` with all fix locations and summary counts. """ all_locations: list[FixLocation] = [] files_affected = 0 diff --git a/cli/fix/commands.py b/cli/fix/commands.py index 233a4e458..7e061af46 100644 --- a/cli/fix/commands.py +++ b/cli/fix/commands.py @@ -18,15 +18,15 @@ def fix_async( ): """Fix async calls for the await_result default change. - Scans Python source files for ``aact``, ``ainstruct``, and ``aquery`` calls + Scans Python source files for `aact`, `ainstruct`, and `aquery` calls and applies an automated migration to restore blocking behaviour after the - ``await_result`` default changed from ``True`` to ``False``. + `await_result` default changed from `True` to `False`. Prerequisites: - Mellea installed (``uv add mellea``). + Mellea installed (`uv add mellea`). Output: - Modifies Python source files in place (unless ``--dry-run``). Prints a + Modifies Python source files in place (unless `--dry-run`). Prints a summary of fixed call sites with file paths and line numbers. Examples: @@ -35,7 +35,7 @@ def fix_async( Args: path: File or directory to scan. mode: Fix strategy to apply. - dry_run: If ``True``, report locations without modifying files. + dry_run: If `True`, report locations without modifying files. Raises: typer.Exit: If *path* does not exist. @@ -99,14 +99,14 @@ def fix_genslots( ): """Rewrite genslot imports and class names to genstub equivalents. - Scans Python source files and replaces deprecated ``GenerativeSlot`` imports - and class references with their ``GenerativeStub`` replacements. + Scans Python source files and replaces deprecated `GenerativeSlot` imports + and class references with their `GenerativeStub` replacements. Prerequisites: - Mellea installed (``uv add mellea``). + Mellea installed (`uv add mellea`). Output: - Modifies Python source files in place (unless ``--dry-run``). Prints a + Modifies Python source files in place (unless `--dry-run`). Prints a summary of rewritten references with file paths and line numbers. Examples: @@ -114,7 +114,7 @@ def fix_genslots( Args: path: File or directory to scan. - dry_run: If ``True``, report locations without modifying files. + dry_run: If `True`, report locations without modifying files. Raises: typer.Exit: If *path* does not exist. diff --git a/cli/fix/genstub_fixer.py b/cli/fix/genstub_fixer.py index 1ea2e8812..f5fc51e8a 100644 --- a/cli/fix/genstub_fixer.py +++ b/cli/fix/genstub_fixer.py @@ -1,11 +1,11 @@ """Line-based detection and rewriting of old genslot imports and class names. Targets: -- ``from mellea.stdlib.components.genslot import ...`` → ``genstub`` -- ``import mellea.stdlib.components.genslot [as ...]`` → ``genstub`` -- ``from mellea.stdlib.components import genslot [as ...]`` → ``genstub`` -- ``from .genslot import ...`` (relative imports) → ``genstub`` -- ``GenerativeSlot`` → ``GenerativeStub`` (and Sync/Async variants) +- `from mellea.stdlib.components.genslot import ...` → `genstub` +- `import mellea.stdlib.components.genslot [as ...]` → `genstub` +- `from mellea.stdlib.components import genslot [as ...]` → `genstub` +- `from .genslot import ...` (relative imports) → `genstub` +- `GenerativeSlot` → `GenerativeStub` (and Sync/Async variants) """ from __future__ import annotations @@ -17,7 +17,7 @@ # Directories to skip during traversal. SKIP_DIRS = {"__pycache__", ".git", ".venv", "node_modules"} -# Ordered longest-first so ``SyncGenerativeSlot`` is replaced before ``GenerativeSlot``. +# Ordered longest-first so `SyncGenerativeSlot` is replaced before `GenerativeSlot`. _CLASS_RENAMES: list[tuple[str, str]] = [ ("AsyncGenerativeSlot", "AsyncGenerativeStub"), ("SyncGenerativeSlot", "SyncGenerativeStub"), @@ -118,7 +118,7 @@ def find_genslot_refs(source: str, filepath: Path) -> list[GenStubFixLocation]: Args: source: Python source text. - filepath: Used for the ``filepath`` field in returned locations. + filepath: Used for the `filepath` field in returned locations. Returns: List of locations that would be changed. @@ -140,7 +140,7 @@ def fix_genslot_file( Args: filepath: Path to the Python file to fix. - dry_run: If ``True``, return locations without modifying the file. + dry_run: If `True`, return locations without modifying the file. Returns: List of locations found (and optionally fixed). @@ -165,7 +165,7 @@ def fix_genslot_path(path: Path, *, dry_run: bool = False) -> GenStubFixResult: Args: path: File or directory to process. - dry_run: If ``True``, report locations without modifying files. + dry_run: If `True`, report locations without modifying files. Returns: Aggregated result with all fix locations and summary counts. diff --git a/cli/m.py b/cli/m.py index 8b3c9e0ba..a233baff8 100644 --- a/cli/m.py +++ b/cli/m.py @@ -1,9 +1,9 @@ -"""Entrypoint for the ``m`` command-line tool. +"""Entrypoint for the `m` command-line tool. -Wires together all CLI sub-applications into a single Typer root command: ``m serve`` -(start a model-serving endpoint), ``m alora`` (train and upload LoRA/aLoRA adapters), -``m decompose`` (LLM-driven task decomposition), and ``m eval`` (test-based model -evaluation). Run ``m --help`` to see all available sub-commands. +Wires together all CLI sub-applications into a single Typer root command: `m serve` +(start a model-serving endpoint), `m alora` (train and upload LoRA/aLoRA adapters), +`m decompose` (LLM-driven task decomposition), and `m eval` (test-based model +evaluation). Run `m --help` to see all available sub-commands. """ try: @@ -28,13 +28,13 @@ def callback() -> None: """Mellea command-line tool for LLM-powered workflows. - Provides sub-commands for serving models (``m serve``), training and uploading - adapters (``m alora``), decomposing tasks into subtasks (``m decompose``), - running test-based evaluation pipelines (``m eval``), and applying automated - code migrations (``m fix``). + Provides sub-commands for serving models (`m serve`), training and uploading + adapters (`m alora`), decomposing tasks into subtasks (`m decompose`), + running test-based evaluation pipelines (`m eval`), and applying automated + code migrations (`m fix`). Prerequisites: - Mellea installed (``uv add mellea``). + Mellea installed (`uv add mellea`). See Also: guide: getting-started/quickstart diff --git a/cli/serve/commands.py b/cli/serve/commands.py index 39bfb3415..6f9021ca3 100644 --- a/cli/serve/commands.py +++ b/cli/serve/commands.py @@ -1,8 +1,8 @@ -"""Typer command definition for ``m serve``. +"""Typer command definition for `m serve`. Separates the CLI interface (typer annotations) from the server implementation -(FastAPI, uvicorn) so that ``m --help`` works without the ``server`` extra installed. -The heavy server dependencies are only imported when ``m serve`` is actually invoked. +(FastAPI, uvicorn) so that `m --help` works without the `server` extra installed. +The heavy server dependencies are only imported when `m serve` is actually invoked. """ import typer @@ -18,18 +18,18 @@ def serve( ): """Serve a Mellea program as an OpenAI-compatible HTTP endpoint. - Loads a Python file containing a ``serve`` function and exposes it + Loads a Python file containing a `serve` function and exposes it via a FastAPI server implementing the OpenAI chat completions API. The server - accepts ``POST /v1/chat/completions`` requests. + accepts `POST /v1/chat/completions` requests. Prerequisites: - Mellea installed with server dependency group (``uv add 'mellea[server]'``). - The python file being loaded must have a ``serve`` function. + Mellea installed with server dependency group (`uv add 'mellea[server]'`). + The python file being loaded must have a `serve` function. Output: Starts a long-running HTTP server on the specified host and port. - The ``/v1/chat/completions`` endpoint accepts OpenAI-format chat - completion requests and returns ``ChatCompletion`` JSON responses. + The `/v1/chat/completions` endpoint accepts OpenAI-format chat + completion requests and returns `ChatCompletion` JSON responses. Examples: m serve my_app.py --port 9000 diff --git a/cli/serve/models.py b/cli/serve/models.py index a775e4c0a..cd06da94a 100644 --- a/cli/serve/models.py +++ b/cli/serve/models.py @@ -58,7 +58,7 @@ class JsonSchemaFormat(BaseModel): """JSON Schema definition.""" strict: bool | None = None - """Accepted for OpenAI compatibility; currently ignored by ``m serve``.""" + """Accepted for OpenAI compatibility; currently ignored by `m serve`.""" model_config = {"populate_by_name": True, "serialize_by_alias": True} diff --git a/cli/serve/schema_converter.py b/cli/serve/schema_converter.py index 4aa5fd519..a829b8615 100644 --- a/cli/serve/schema_converter.py +++ b/cli/serve/schema_converter.py @@ -11,41 +11,41 @@ def json_schema_to_pydantic( """Convert a practical subset of JSON Schema to a Pydantic model dynamically. This converter targets the OpenAI-style structured output schemas used by - ``m serve``. It intentionally maps JSON Schema features into Python typing + `m serve`. It intentionally maps JSON Schema features into Python typing and Pydantic model semantics rather than attempting to preserve every JSON Schema validation rule exactly. Supported features: - - top-level and nested ``object`` schemas with ``properties`` and ``required`` - - primitive types: ``string``, ``integer``, ``number``, ``boolean`` - - arrays via ``type: "array"`` with supported ``items`` - - string or primitive enums via ``enum`` - - nullable fields via ``type: ["", "null"]`` - - local ``$ref`` into ``$defs`` / ``definitions`` - - simple ``allOf`` merging for object-like schemas - - simple ``anyOf`` / ``oneOf`` unions when each branch is representable - - boolean and schema-valued ``additionalProperties`` + - top-level and nested `object` schemas with `properties` and `required` + - primitive types: `string`, `integer`, `number`, `boolean` + - arrays via `type: "array"` with supported `items` + - string or primitive enums via `enum` + - nullable fields via `type: ["", "null"]` + - local `$ref` into `$defs` / `definitions` + - simple `allOf` merging for object-like schemas + - simple `anyOf` / `oneOf` unions when each branch is representable + - boolean and schema-valued `additionalProperties` Behavior notes: - - ``additionalProperties: false`` maps to ``extra="forbid"`` - - ``additionalProperties: true`` maps to ``extra="ignore"`` - - schema-valued ``additionalProperties`` maps to ``dict[str, ValueType]`` + - `additionalProperties: false` maps to `extra="forbid"` + - `additionalProperties: true` maps to `extra="ignore"` + - schema-valued `additionalProperties` maps to `dict[str, ValueType]` only for open-ended object maps. It cannot be combined with named - ``properties`` because that is not representable as a single standard + `properties` because that is not representable as a single standard Pydantic field shape without custom validators. - - sibling keywords next to ``$ref`` are merged over the resolved target, + - sibling keywords next to `$ref` are merged over the resolved target, matching common JSON Schema practice for OpenAI-compatible schemas - Still unsupported and will raise ``ValueError``: + Still unsupported and will raise `ValueError`: - non-local refs - - recursive ``$ref`` cycles + - recursive `$ref` cycles - tuple-style array schemas - - object schemas without ``properties`` unless they are pure - ``additionalProperties`` maps + - object schemas without `properties` unless they are pure + `additionalProperties` maps - schema constraints beyond representable typing/extra handling Args: - schema: JSON Schema definition (must have top-level ``type: "object"``). + schema: JSON Schema definition (must have top-level `type: "object"`). model_name: Name for the generated Pydantic model. Returns: @@ -105,7 +105,7 @@ def _resolve_ref(ref: str) -> tuple[str, dict[str, Any]]: ) def _merge_nullable(annotation: Any, is_nullable: bool) -> Any: - """Wrap an annotation in ``None`` when the source schema is nullable.""" + """Wrap an annotation in `None` when the source schema is nullable.""" if is_nullable: return annotation | None return annotation @@ -133,9 +133,9 @@ def _enum_annotation(enum_values: list[Any], path: str) -> Any: def _merge_object_schemas( schemas: list[dict[str, Any]], path: str ) -> dict[str, Any]: - """Merge simple object schemas for ``allOf``. + """Merge simple object schemas for `allOf`. - This supports the common OpenAI-compatible case where ``allOf`` is used + This supports the common OpenAI-compatible case where `allOf` is used to compose object fragments. Conflicting keywords are rejected rather than silently guessed. """ @@ -201,7 +201,7 @@ def _merge_object_schemas( def _union_annotation( keyword: str, union_schemas: list[dict[str, Any]], path: str ) -> Any: - """Convert ``anyOf``/``oneOf`` branches into a Python union annotation.""" + """Convert `anyOf`/`oneOf` branches into a Python union annotation.""" if not union_schemas: raise ValueError(f"{_format_path(path)} {keyword} must not be empty") diff --git a/cli/serve/streaming.py b/cli/serve/streaming.py index 02ee3bc75..458832ddd 100644 --- a/cli/serve/streaming.py +++ b/cli/serve/streaming.py @@ -44,13 +44,13 @@ async def stream_chat_completion_chunks( created: Unix timestamp of when the completion was created. stream_options: OpenAI-compatible streaming options. Controls whether usage statistics are included in the final chunk via the - ``include_usage`` field. + `include_usage` field. system_fingerprint: Backend configuration fingerprint to include in chunks. - Defaults to ``None``. + Defaults to `None`. Yields: Server-sent event payload strings representing OpenAI-compatible chat - completion chunks, including the terminating ``[DONE]`` event. + completion chunks, including the terminating `[DONE]` event. """ try: initial_chunk = ChatCompletionChunk( diff --git a/mellea/backends/__init__.py b/mellea/backends/__init__.py index 65e8d1e63..672827818 100644 --- a/mellea/backends/__init__.py +++ b/mellea/backends/__init__.py @@ -1,10 +1,10 @@ """Backend implementations for the mellea inference layer. This package exposes the concrete machinery for connecting mellea to language model -servers. It bundles ``FormatterBackend`` (a prompt-engineering base class for legacy -models), ``ModelIdentifier`` (portable cross-platform model names), ``ModelOption`` -(generation parameters such as token limits), ``SimpleLRUCache`` (KV-cache -management), and ``MelleaTool`` / ``tool`` (LLM tool definitions). Reach for this +servers. It bundles `FormatterBackend` (a prompt-engineering base class for legacy +models), `ModelIdentifier` (portable cross-platform model names), `ModelOption` +(generation parameters such as token limits), `SimpleLRUCache` (KV-cache +management), and `MelleaTool` / `tool` (LLM tool definitions). Reach for this package when configuring a backend, declaring tools, or tuning inference options. """ diff --git a/mellea/backends/backend.py b/mellea/backends/backend.py index b8329392d..3d60c4be7 100644 --- a/mellea/backends/backend.py +++ b/mellea/backends/backend.py @@ -1,10 +1,10 @@ -"""``FormatterBackend``: base class for prompt-engineering backends. +"""`FormatterBackend`: base class for prompt-engineering backends. -``FormatterBackend`` extends the abstract ``Backend`` with a ``ChatFormatter`` and -a ``ModelIdentifier``, bridging mellea's generative programming primitives to models +`FormatterBackend` extends the abstract `Backend` with a `ChatFormatter` and +a `ModelIdentifier`, bridging mellea's generative programming primitives to models that do not yet natively support spans or structured fine-tuning. Concrete backend -implementations (e.g. Ollama, HuggingFace, OpenAI) subclass ``FormatterBackend`` and -supply the model-specific ``generate_from_context`` logic. +implementations (e.g. Ollama, HuggingFace, OpenAI) subclass `FormatterBackend` and +supply the model-specific `generate_from_context` logic. """ import abc @@ -31,7 +31,7 @@ class FormatterBackend(Backend, abc.ABC): Args: model_id (str | ModelIdentifier): The model identifier to use for generation. formatter (ChatFormatter): The formatter used to convert components into prompts. - model_options (dict | None): Default model options; if ``None``, an empty dict + model_options (dict | None): Default model options; if `None`, an empty dict is used. """ diff --git a/mellea/backends/bedrock.py b/mellea/backends/bedrock.py index a340dba94..500d5d4a0 100644 --- a/mellea/backends/bedrock.py +++ b/mellea/backends/bedrock.py @@ -25,7 +25,7 @@ def list_mantle_models(region: str | None = None) -> list: """Return all models available at a bedrock-mantle endpoint. Args: - region: AWS region name (e.g. ``"us-east-1"``), or ``None`` to use the + region: AWS region name (e.g. `"us-east-1"`), or `None` to use the default region. Returns: @@ -43,10 +43,10 @@ def stringify_mantle_model_ids(region: str | None = None) -> str: """Return a human-readable list of all models available at the mantle endpoint for an AWS region. Args: - region: AWS region name, or ``None`` to use the default region. + region: AWS region name, or `None` to use the default region. Returns: - Newline-separated string of model IDs prefixed with ``" * "``. + Newline-separated string of model IDs prefixed with `" * "`. """ models = list_mantle_models() model_names = "\n * ".join([str(m.id) for m in models]) @@ -60,19 +60,19 @@ def create_bedrock_mantle_backend( Args: model_id (ModelIdentifier | str): The model to use, either as a - ``ModelIdentifier`` (which must have a ``bedrock_name``) or a raw + `ModelIdentifier` (which must have a `bedrock_name`) or a raw Bedrock model ID string. - region (str | None): AWS region name, or ``None`` to use the default - region (``"us-east-1"``). + region (str | None): AWS region name, or `None` to use the default + region (`"us-east-1"`). Returns: - OpenAIBackend: An ``OpenAIBackend`` configured to call the specified model + OpenAIBackend: An `OpenAIBackend` configured to call the specified model via AWS Bedrock Mantle. Raises: - Exception: If ``model_id`` is a ``ModelIdentifier`` with no ``bedrock_name`` + Exception: If `model_id` is a `ModelIdentifier` with no `bedrock_name` set. - AssertionError: If the ``AWS_BEARER_TOKEN_BEDROCK`` environment variable is + AssertionError: If the `AWS_BEARER_TOKEN_BEDROCK` environment variable is not set. Exception: If the specified model is not available in the target region. """ diff --git a/mellea/backends/cache.py b/mellea/backends/cache.py index 81f7a7ab3..13393b7f1 100644 --- a/mellea/backends/cache.py +++ b/mellea/backends/cache.py @@ -1,9 +1,9 @@ """Cache abstractions and implementations for model state. -Defines the abstract ``Cache`` interface with ``put``, ``get``, and -``current_size`` methods, and provides a concrete ``SimpleLRUCache`` that evicts +Defines the abstract `Cache` interface with `put`, `get`, and +`current_size` methods, and provides a concrete `SimpleLRUCache` that evicts the least-recently-used entry when capacity is exceeded — optionally calling an -``on_evict`` callback (e.g. to free GPU memory). Used by local HuggingFace backends +`on_evict` callback (e.g. to free GPU memory). Used by local HuggingFace backends to store and reuse KV cache state across requests. """ @@ -40,7 +40,7 @@ def get(self, key: str | int) -> Any | None: key (str | int): The cache key to look up. Returns: - Any | None: The cached value, or ``None`` if ``key`` has no cached entry. + Any | None: The cached value, or `None` if `key` has no cached entry. """ ... @@ -58,7 +58,7 @@ class SimpleLRUCache(Cache): """A simple `LRU `_ cache. Evicts the least-recently-used entry when capacity is exceeded, optionally - invoking an ``on_evict`` callback (e.g. to free GPU memory). Used by local + invoking an `on_evict` callback (e.g. to free GPU memory). Used by local HuggingFace backends to store and reuse KV cache state across requests. Args: @@ -92,7 +92,7 @@ def get(self, key: str | int) -> Any | None: key (str | int): The cache key to look up. Returns: - Any | None: The cached value, or ``None`` if ``key`` is not present. + Any | None: The cached value, or `None` if `key` is not present. """ if key not in self.cache: return None @@ -106,7 +106,7 @@ def put(self, key: str | int, value: Any) -> None: """Insert or update a value in the cache. If the cache is at capacity and the key is new, the least-recently-used - entry is evicted first, invoking the ``on_evict`` callback if set. + entry is evicted first, invoking the `on_evict` callback if set. Args: key (str | int): The cache key to store the value under. diff --git a/mellea/backends/dummy.py b/mellea/backends/dummy.py index 81c7ca2c3..691676cc9 100644 --- a/mellea/backends/dummy.py +++ b/mellea/backends/dummy.py @@ -14,18 +14,18 @@ class DummyBackend(Backend): """A backend for smoke testing. - Returns predetermined string responses in sequence, or ``"dummy"`` if no + Returns predetermined string responses in sequence, or `"dummy"` if no responses are provided. Intended for unit tests and integration smoke tests where real model inference is not needed. Args: responses (list[str] | None): Ordered list of strings to return on - successive ``generate_from_context`` calls, or ``None`` to always - return ``"dummy"``. + successive `generate_from_context` calls, or `None` to always + return `"dummy"`. Attributes: - idx (int): Index of the next response to return from ``responses``; - starts at ``0`` and increments on each call. + idx (int): Index of the next response to return from `responses`; + starts at `0` and increments on each call. """ def __init__(self, responses: list[str] | None): @@ -42,16 +42,16 @@ async def _generate_from_context( model_options: dict | None = None, tool_calls: bool = False, ) -> tuple[ModelOutputThunk[C], Context]: - """Return the next predetermined response for ``action`` given ``ctx``. + """Return the next predetermined response for `action` given `ctx`. - If ``responses`` is ``None``, always returns the string ``"dummy"``. - Otherwise returns the next item from ``responses`` in order. + If `responses` is `None`, always returns the string `"dummy"`. + Otherwise returns the next item from `responses` in order. Args: action (Component[C] | CBlock): The component or content block to generate a completion for. ctx (Context): The current generation context. - format (type[BaseModelSubclass] | None): Must be ``None``; constrained + format (type[BaseModelSubclass] | None): Must be `None`; constrained decoding is not supported. model_options (dict | None): Ignored by this backend. tool_calls (bool): Ignored by this backend. @@ -61,8 +61,8 @@ async def _generate_from_context( response and an updated context. Raises: - AssertionError: If ``format`` is not ``None``. - Exception: If all responses from ``responses`` have been consumed. + AssertionError: If `format` is not `None`. + Exception: If all responses from `responses` have been consumed. """ assert format is None, "The DummyBackend does not support constrained decoding." if self.responses is None: diff --git a/mellea/backends/huggingface.py b/mellea/backends/huggingface.py index 69e858304..c96bab4e0 100644 --- a/mellea/backends/huggingface.py +++ b/mellea/backends/huggingface.py @@ -83,9 +83,9 @@ class _EventStoppingCriteria(StoppingCriteria): """StoppingCriteria that signals the model to stop when a threading.Event is set. Used by LocalHFBackend to implement cooperative cancellation: when - ``cancel_generation`` is called, it sets the backing event via - ``_cancel_hook`` before cancelling the asyncio task, giving the HF - ``model.generate`` thread a chance to exit cleanly rather than running + `cancel_generation` is called, it sets the backing event via + `_cancel_hook` before cancelling the asyncio task, giving the HF + `model.generate` thread a chance to exit cleanly rather than running to completion. """ @@ -101,12 +101,12 @@ def _install_cancel_stopping_criteria( ) -> threading.Event: """Wire a cooperative-cancel event into the generate call's stopping criteria. - Pops any caller-supplied ``stopping_criteria`` from *generate_options* (to - avoid passing it twice via both ``**generate_options`` and - ``**streaming_kwargs``), prepends an :class:`_EventStoppingCriteria` backed - by a fresh ``threading.Event``, and stores the merged list in + Pops any caller-supplied `stopping_criteria` from *generate_options* (to + avoid passing it twice via both `**generate_options` and + `**streaming_kwargs`), prepends an :class:`_EventStoppingCriteria` backed + by a fresh `threading.Event`, and stores the merged list in *streaming_kwargs*. Returns the event so the caller can arm - ``output._cancel_hook = event.set``. + `output._cancel_hook = event.set`. """ cancel_event = threading.Event() user_sc = generate_options.pop("stopping_criteria", None) @@ -130,25 +130,25 @@ def _install_cancel_stopping_criteria( class HFAloraCacheInfo: """A dataclass for holding a KV cache and associated generation metadata. - Used by ``LocalHFBackend`` to store intermediate model state that can be + Used by `LocalHFBackend` to store intermediate model state that can be reused across generation requests via an LRU cache. Args: - kv_cache (DynamicCache | None): The HuggingFace ``DynamicCache`` holding - precomputed key/value tensors, or ``None`` if not available. + kv_cache (DynamicCache | None): The HuggingFace `DynamicCache` holding + precomputed key/value tensors, or `None` if not available. merged_token_ids (Any): Token IDs corresponding to the cached prefix. merged_attention (Any): Attention mask for the cached prefix tokens. q_end (int): Index of the last prompt token in the merged token sequence; - defaults to ``-1``. + defaults to `-1`. scores (Any): Optional logit scores from the generation step; defaults to - ``None``. + `None`. Attributes: kv_cache (DynamicCache | None): The cached key/value tensors. merged_token_ids (Any): Token IDs for the cached prefix. merged_attention (Any): Attention mask for the cached prefix. q_end (int): End index of the prompt portion in merged token IDs. - scores (Any): Logit scores from generation, or ``None``. + scores (Any): Logit scores from generation, or `None`. """ kv_cache: DynamicCache | None @@ -270,22 +270,22 @@ class LocalHFBackend(FormatterBackend, AdapterMixin): Args: model_id (str | ModelIdentifier): Used to load the model and tokenizer via - HuggingFace ``Auto*`` classes. + HuggingFace `Auto*` classes. formatter (ChatFormatter | None): Formatter for rendering components into - prompts. Defaults to ``TemplateFormatter``. - use_caches (bool): If ``False``, KV caching is disabled even if a ``Cache`` + prompts. Defaults to `TemplateFormatter`. + use_caches (bool): If `False`, KV caching is disabled even if a `Cache` is provided. cache (Cache | None): Caching strategy; defaults to - ``SimpleLRUCache(0, on_evict=_cleanup_kv_cache)``. + `SimpleLRUCache(0, on_evict=_cleanup_kv_cache)`. custom_config (TransformersTorchConfig | None): Override for - tokenizer/model/device; if provided, ``model_id`` is not used for loading. - default_to_constraint_checking_alora (bool): If ``False``, aLoRA constraint + tokenizer/model/device; if provided, `model_id` is not used for loading. + default_to_constraint_checking_alora (bool): If `False`, aLoRA constraint checking is deactivated; mainly for benchmarking and debugging. model_options (dict | None): Default model options for generation requests. Attributes: to_mellea_model_opts_map (dict): Mapping from HF-specific option names to - Mellea ``ModelOption`` sentinel keys. + Mellea `ModelOption` sentinel keys. from_mellea_model_opts_map (dict): Mapping from Mellea sentinel keys to HF-specific option names. """ @@ -387,8 +387,8 @@ def __init__( def _get_hf_model_id(self) -> str: """Return the HuggingFace model name as a string. - Returns the ``hf_model_name`` attribute when a ``ModelIdentifier`` is - provided, otherwise casts ``model_id`` to ``str``. + Returns the `hf_model_name` attribute when a `ModelIdentifier` is + provided, otherwise casts `model_id` to `str`. """ if hasattr(self.model_id, "hf_model_name"): return str(self.model_id.hf_model_name) # type: ignore @@ -414,9 +414,9 @@ async def _generate_from_context( model_options: dict | None = None, tool_calls: bool = False, ) -> tuple[ModelOutputThunk[C], Context]: - """Generate a completion for ``action`` given ``ctx`` using the HuggingFace model. + """Generate a completion for `action` given `ctx` using the HuggingFace model. - Automatically routes ``Requirement`` and ``Intrinsic`` actions to their + Automatically routes `Requirement` and `Intrinsic` actions to their corresponding aLoRA adapters when available. Args: @@ -427,12 +427,12 @@ async def _generate_from_context( structured/constrained output decoding via llguidance. model_options (dict | None): Per-call model options that override the backend's defaults. - tool_calls (bool): If ``True``, expose available tools to the model and + tool_calls (bool): If `True`, expose available tools to the model and parse tool-call responses. Returns: tuple[ModelOutputThunk[C], Context]: A thunk holding the (lazy) model output - and an updated context that includes ``action`` and the new output. + and an updated context that includes `action` and the new output. """ span = start_generate_span( backend=self, action=action, ctx=ctx, format=format, tool_calls=tool_calls @@ -552,7 +552,7 @@ async def _generate_from_intrinsic( """Generate a completion for an intrinsic action using an adapter. Applies the intrinsic's I/O rewriter to transform the conversation, - injects ``intrinsic_name`` into ``chat_template_kwargs`` so that the + injects `intrinsic_name` into `chat_template_kwargs` so that the Granite Switch chat template activates the correct adapter, and post-processes the model output through the intrinsic's result processor. @@ -565,7 +565,7 @@ async def _generate_from_intrinsic( action (Intrinsic): The intrinsic component to execute. ctx (Context): The current generation context (must be a chat context). model_options (dict[str, Any]): Merged model options for this call. - tool_calls (bool): If ``True``, expose available tools to the model + tool_calls (bool): If `True`, expose available tools to the model and parse tool-call responses. Returns: @@ -1211,8 +1211,8 @@ async def processing( """Accumulate decoded text from a streaming chunk or full generation output. For streaming responses the chunk is an already-decoded string from - ``AsyncTextIteratorStreamer``; for non-streaming it is a - ``GenerateDecoderOnlyOutput`` that is decoded here. + `AsyncTextIteratorStreamer`; for non-streaming it is a + `GenerateDecoderOnlyOutput` that is decoded here. Args: mot (ModelOutputThunk): The output thunk being populated. @@ -1258,7 +1258,7 @@ async def post_processing( class used during generation, if any. tool_calls (bool): Whether tool calling was enabled for this request. tools (dict[str, AbstractMelleaTool]): Available tools, keyed by name. - seed: The random seed used during generation, or ``None``. + seed: The random seed used during generation, or `None`. input_ids: The prompt token IDs; used to compute token counts and for KV cache bookkeeping. """ @@ -1420,7 +1420,7 @@ async def generate_from_raw( """Generate completions for multiple actions without chat templating. Passes formatted prompt strings directly to the HuggingFace model's - ``generate`` method as a batch. Tool calling is not supported. + `generate` method as a batch. Tool calling is not supported. Args: actions (Sequence[Component[C] | CBlock]): Actions to generate completions for. @@ -1538,20 +1538,20 @@ async def generate_from_raw( # region cache management def cache_get(self, id: str | int) -> HFAloraCacheInfo | None: - """Retrieve a cached ``HFAloraCacheInfo`` entry by its key. + """Retrieve a cached `HFAloraCacheInfo` entry by its key. Args: id (str | int): The cache key to look up. Returns: - HFAloraCacheInfo | None: The cached entry, or ``None`` if not found. + HFAloraCacheInfo | None: The cached entry, or `None` if not found. """ v = self._cache.get(id) assert v is None or type(v) is HFAloraCacheInfo return v def cache_put(self, id: str | int, v: HFAloraCacheInfo): - """Store an ``HFAloraCacheInfo`` entry in the cache under the given key. + """Store an `HFAloraCacheInfo` entry in the cache under the given key. Args: id (str | int): The cache key to store the entry under. @@ -1642,7 +1642,7 @@ def base_model_name(self): def add_adapter(self, adapter: LocalHFAdapter): """Register a LoRA/aLoRA adapter with this backend so it can be loaded later. - Downloads the adapter weights (via ``adapter.get_local_hf_path``) and records + Downloads the adapter weights (via `adapter.get_local_hf_path`) and records the adapter in the backend's registry. The adapter must not already be registered with a different backend. @@ -1650,7 +1650,7 @@ def add_adapter(self, adapter: LocalHFAdapter): adapter (LocalHFAdapter): The adapter to register with this backend. Raises: - Exception: If ``adapter`` has already been added to a different backend. + Exception: If `adapter` has already been added to a different backend. """ if adapter.backend is not None: if adapter.backend is self: @@ -1676,12 +1676,12 @@ def add_adapter(self, adapter: LocalHFAdapter): def load_adapter(self, adapter_qualified_name: str): """Load a previously registered adapter into the underlying HuggingFace model. - The adapter must have been registered via ``add_adapter`` first. Do not call + The adapter must have been registered via `add_adapter` first. Do not call this method while generation requests are in progress. Args: - adapter_qualified_name (str): The ``adapter.qualified_name`` of the adapter - to load (i.e. ``"_"``) + adapter_qualified_name (str): The `adapter.qualified_name` of the adapter + to load (i.e. `"_"`) Raises: ValueError: If no adapter with the given qualified name has been added to @@ -1724,7 +1724,7 @@ def unload_adapter(self, adapter_qualified_name: str): method returns without error. Args: - adapter_qualified_name (str): The ``adapter.qualified_name`` of the adapter + adapter_qualified_name (str): The `adapter.qualified_name` of the adapter to unload. """ # Check if the backend knows about this adapter. @@ -1744,8 +1744,8 @@ def list_adapters(self) -> list[str]: """List the qualified names of all adapters currently loaded in this backend. Returns: - list[str]: Qualified adapter names (i.e. ``adapter.qualified_name``) for - all adapters that have been loaded via ``load_adapter``. + list[str]: Qualified adapter names (i.e. `adapter.qualified_name`) for + all adapters that have been loaded via `load_adapter`. """ return list(self._loaded_adapters.keys()) diff --git a/mellea/backends/kv_block_helpers.py b/mellea/backends/kv_block_helpers.py index 077781b9d..b3a8e5dd9 100644 --- a/mellea/backends/kv_block_helpers.py +++ b/mellea/backends/kv_block_helpers.py @@ -1,8 +1,8 @@ """Low-level utilities for concatenating transformer KV caches (KV smashing). -Provides functions for merging ``DynamicCache`` and legacy tuple caches along the -time axis (``merge_dynamic_caches``, ``legacy_cache_smash``), and -``tokens_to_legacy_cache`` for converting a tokenized prompt into a prefilled KV +Provides functions for merging `DynamicCache` and legacy tuple caches along the +time axis (`merge_dynamic_caches`, `legacy_cache_smash`), and +`tokens_to_legacy_cache` for converting a tokenized prompt into a prefilled KV cache. These helpers are used internally by local HuggingFace backends that reuse cached prefix computations across multiple generation calls. """ @@ -31,10 +31,10 @@ def legacy_cache_smash(a: LegacyCache, b: LegacyCache) -> LegacyCache: Args: a: First legacy KV cache (tuple of per-layer (K, V) tensor pairs). - b: Second legacy KV cache to concatenate after ``a``. + b: Second legacy KV cache to concatenate after `a`. Returns: - New legacy cache with ``b`` appended to ``a`` along the sequence dimension. + New legacy cache with `b` appended to `a` along the sequence dimension. """ legacy_merged = tuple( (torch.cat([a[i][0], b[i][0]], dim=2), torch.cat([a[i][1], b[i][1]], dim=2)) @@ -47,10 +47,10 @@ def merge_dynamic_caches(caches: Iterable[DynamicCache]) -> DynamicCache: """Merges two DynamicCache Ks and Vs along the time axis. Args: - caches: Iterable of ``DynamicCache`` objects to merge in order. + caches: Iterable of `DynamicCache` objects to merge in order. Returns: - A single ``DynamicCache`` with all caches concatenated along the sequence dimension. + A single `DynamicCache` with all caches concatenated along the sequence dimension. """ legacies = [c.to_legacy_cache() for c in caches] # type: ignore assert len(legacies) >= 1 @@ -65,9 +65,9 @@ def tokens_to_legacy_cache( Args: model: The HuggingFace model used for prefill. - device: Target device string (e.g. ``"cuda"``, ``"cpu"``). - tokens_or_cache: Either a ``BatchEncoding`` to prefill, or an existing - ``DynamicCache`` to convert directly. + device: Target device string (e.g. `"cuda"`, `"cpu"`). + tokens_or_cache: Either a `BatchEncoding` to prefill, or an existing + `DynamicCache` to convert directly. Returns: Legacy KV cache representation as a tuple of per-layer (K, V) tensor pairs. diff --git a/mellea/backends/litellm.py b/mellea/backends/litellm.py index 49cd99c05..ba2be29b4 100644 --- a/mellea/backends/litellm.py +++ b/mellea/backends/litellm.py @@ -63,17 +63,17 @@ class LiteLLMBackend(FormatterBackend): Args: model_id (str): The LiteLLM model identifier string; typically - ``"//"``. + `"//"`. formatter (ChatFormatter | None): Formatter for rendering components. - Defaults to ``TemplateFormatter``. + Defaults to `TemplateFormatter`. base_url (str | None): Base URL for the LLM API endpoint; defaults to the Ollama local endpoint. model_options (dict | None): Default model options for generation requests. Attributes: to_mellea_model_opts_map (dict): Mapping from backend-specific option names to - Mellea ``ModelOption`` sentinel keys. - from_mellea_model_opts_map (dict): Mapping from Mellea ``ModelOption`` sentinel + Mellea `ModelOption` sentinel keys. + from_mellea_model_opts_map (dict): Mapping from Mellea `ModelOption` sentinel keys to backend-specific option names. """ @@ -142,10 +142,10 @@ async def _generate_from_context( model_options: dict | None = None, tool_calls: bool = False, ) -> tuple[ModelOutputThunk[C], Context]: - """Generate a completion for ``action`` given ``ctx`` via the LiteLLM chat API. + """Generate a completion for `action` given `ctx` via the LiteLLM chat API. - Delegates to ``_generate_from_chat_context_standard``. Only chat contexts are - supported; raises ``NotImplementedError`` otherwise. + Delegates to `_generate_from_chat_context_standard`. Only chat contexts are + supported; raises `NotImplementedError` otherwise. Args: action (Component[C] | CBlock): The component or content block to generate @@ -155,12 +155,12 @@ async def _generate_from_context( structured/constrained output decoding. model_options (dict | None): Per-call model options that override the backend's defaults. - tool_calls (bool): If ``True``, expose available tools to the model and + tool_calls (bool): If `True`, expose available tools to the model and parse tool-call responses. Returns: tuple[ModelOutputThunk[C], Context]: A thunk holding the (lazy) model output - and an updated context that includes ``action`` and the new output. + and an updated context that includes `action` and the new output. """ assert ctx.is_chat_context, NotImplementedError( "The Openai backend only supports chat-like contexts." @@ -413,9 +413,9 @@ async def processing( ): """Accumulate content and thinking tokens from a single LiteLLM response chunk. - Called during generation for each ``ModelResponse`` (non-streaming) or - ``ModelResponseStream`` chunk (streaming). Tool call parsing is deferred to - ``post_processing``. + Called during generation for each `ModelResponse` (non-streaming) or + `ModelResponseStream` chunk (streaming). Tool call parsing is deferred to + `post_processing`. Args: mot (ModelOutputThunk): The output thunk being populated. @@ -493,7 +493,7 @@ async def post_processing( used for logging. tools (dict[str, AbstractMelleaTool]): Available tools, keyed by name. thinking: The thinking/reasoning effort level passed to the model, or - ``None`` if reasoning mode was not enabled. + `None` if reasoning mode was not enabled. _format: The structured output format class used during generation, if any. """ # Reconstruct the chat_response from chunks if streamed. @@ -641,7 +641,7 @@ async def generate_from_raw( actions (Sequence[Component[C] | CBlock]): Actions to generate completions for. ctx (Context): The current generation context. format (type[BaseModelSubclass] | None): Optional Pydantic model for - structured output; passed as ``guided_json`` in the request body. + structured output; passed as `guided_json` in the request body. model_options (dict | None): Per-call model options. tool_calls (bool): Ignored; tool calling is not supported on this endpoint. diff --git a/mellea/backends/model_ids.py b/mellea/backends/model_ids.py index 3bcf414d8..56e951127 100644 --- a/mellea/backends/model_ids.py +++ b/mellea/backends/model_ids.py @@ -1,6 +1,6 @@ -"""``ModelIdentifier`` dataclass and a catalog of pre-defined model IDs. +"""`ModelIdentifier` dataclass and a catalog of pre-defined model IDs. -``ModelIdentifier`` is a frozen dataclass that groups the platform-specific name +`ModelIdentifier` is a frozen dataclass that groups the platform-specific name variants for a model (HuggingFace, Ollama, WatsonX, MLX, OpenAI, Bedrock) so that a single constant can be passed to any backend without manual string translation. The module also ships a curated catalog of ready-to-use constants for popular @@ -19,13 +19,13 @@ class ModelIdentifier: 2. Using raw strings is annoying because: no autocomplete, typos, hallucinated names, mismatched model and tokenizer names, etc. Args: - hf_model_name (str | None): HuggingFace Hub model repository ID (e.g. ``"ibm-granite/granite-3.3-8b-instruct"``). - ollama_name (str | None): Ollama model tag (e.g. ``"granite3.3:8b"``). - watsonx_name (str | None): WatsonX AI model ID (e.g. ``"ibm/granite-3-2b-instruct"``). + hf_model_name (str | None): HuggingFace Hub model repository ID (e.g. `"ibm-granite/granite-3.3-8b-instruct"`). + ollama_name (str | None): Ollama model tag (e.g. `"granite3.3:8b"`). + watsonx_name (str | None): WatsonX AI model ID (e.g. `"ibm/granite-3-2b-instruct"`). mlx_name (str | None): MLX model identifier for Apple Silicon inference. - openai_name (str | None): OpenAI API model name (e.g. ``"gpt-5.1"``). - bedrock_name (str | None): AWS Bedrock model ID (e.g. ``"openai.gpt-oss-20b"``). - hf_tokenizer_name (str | None): HuggingFace tokenizer ID; defaults to ``hf_model_name`` if ``None``. + openai_name (str | None): OpenAI API model name (e.g. `"gpt-5.1"`). + bedrock_name (str | None): AWS Bedrock model ID (e.g. `"openai.gpt-oss-20b"`). + hf_tokenizer_name (str | None): HuggingFace tokenizer ID; defaults to `hf_model_name` if `None`. """ diff --git a/mellea/backends/model_options.py b/mellea/backends/model_options.py index 628c2d2ac..eaefa8fca 100644 --- a/mellea/backends/model_options.py +++ b/mellea/backends/model_options.py @@ -21,7 +21,7 @@ class ModelOption: ``` Attributes: - TOOLS (str): Sentinel key for a list or dict of ``MelleaTool`` instances to expose for tool calling. + TOOLS (str): Sentinel key for a list or dict of `MelleaTool` instances to expose for tool calling. TOOL_CHOICE (str): Key for tool choice strategy (passed through to the backend). MAX_NEW_TOKENS (str): Sentinel key for the maximum number of new tokens to generate. SYSTEM_PROMPT (str): Sentinel key for the system prompt string. @@ -30,12 +30,12 @@ class ModelOption: THINKING (str): Sentinel key for enabling/configuring reasoning/thinking mode. SEED (str): Sentinel key for the random seed for reproducible generation. STREAM (str): Sentinel key for enabling streaming responses. - STOP_SEQUENCES (str): Sentinel key for a ``list[str]`` of strings that, when + STOP_SEQUENCES (str): Sentinel key for a `list[str]` of strings that, when encountered in the model output, cause generation to halt. """ TOOLS = "@@@tools@@@" - """Must be a list[MelleaTool] or a dict[str, MelleaTool]. Use ``MelleaTool.from_callable()`` or the ``@tool`` decorator to wrap plain callables.""" + """Must be a list[MelleaTool] or a dict[str, MelleaTool]. Use `MelleaTool.from_callable()` or the `@tool` decorator to wrap plain callables.""" TOOL_CHOICE = "tool_choice" """Controls which tool the model should use. Can be "none", "auto", or a specific tool name.""" @@ -48,16 +48,16 @@ class ModelOption: SEED = "@@@seed@@@" STREAM = "@@@stream@@@" STOP_SEQUENCES = "@@@stop_sequences@@@" - """Must be a ``list[str]``. Generation halts when the model emits any of these strings. + """Must be a `list[str]`. Generation halts when the model emits any of these strings. - Backends translate this to their native parameter (``stop`` for OpenAI/Ollama/LiteLLM - chat backends, ``stop_strings`` for HuggingFace, ``stop_sequences`` for the WatsonX + Backends translate this to their native parameter (`stop` for OpenAI/Ollama/LiteLLM + chat backends, `stop_strings` for HuggingFace, `stop_sequences` for the WatsonX text-generation endpoint). """ @staticmethod def replace_keys(options: dict, from_to: dict[str, str]) -> dict[str, Any]: - """Return a new dict with selected keys in ``options`` renamed according to ``from_to``. + """Return a new dict with selected keys in `options` renamed according to `from_to`. Returns a new dict with the keys in `options` replaced with the corresponding value for that key in `from_to`. @@ -129,16 +129,16 @@ def replace_keys(options: dict, from_to: dict[str, str]) -> dict[str, Any]: @staticmethod def remove_special_keys(model_options: dict[str, Any]) -> dict[str, Any]: - """Return a copy of ``model_options`` with all sentinel-valued keys removed. + """Return a copy of `model_options` with all sentinel-valued keys removed. - Sentinel keys are those whose names start with ``@@@`` (e.g. ``ModelOption.TOOLS``). + Sentinel keys are those whose names start with `@@@` (e.g. `ModelOption.TOOLS`). These are Mellea-internal keys that must not be forwarded to backend APIs. Args: model_options (dict[str, Any]): A model options dictionary that may contain sentinel keys. Returns: - dict[str, Any]: A new dictionary with all ``@@@``-prefixed keys omitted. + dict[str, Any]: A new dictionary with all `@@@`-prefixed keys omitted. """ new_options = {} for k, v in model_options.items(): @@ -150,7 +150,7 @@ def remove_special_keys(model_options: dict[str, Any]) -> dict[str, Any]: def merge_model_options( persistent_opts: dict[str, Any], overwrite_opts: dict[str, Any] | None ) -> dict[str, Any]: - """Merge two model-options dicts, with ``overwrite_opts`` taking precedence on conflicts. + """Merge two model-options dicts, with `overwrite_opts` taking precedence on conflicts. Creates a new dict that contains all keys and values from persistent opts and overwrite opts. If there are duplicate keys, overwrite opts key value pairs will be used. @@ -158,7 +158,7 @@ def merge_model_options( Args: persistent_opts (dict[str, Any]): Base model options (lower precedence). overwrite_opts (dict[str, Any] | None): Per-call model options that override - ``persistent_opts`` on key conflicts; ``None`` is treated as empty. + `persistent_opts` on key conflicts; `None` is treated as empty. Returns: dict[str, Any]: A new merged dictionary. diff --git a/mellea/backends/ollama.py b/mellea/backends/ollama.py index b8d720fb2..e6c70afb7 100644 --- a/mellea/backends/ollama.py +++ b/mellea/backends/ollama.py @@ -44,22 +44,22 @@ class OllamaModelBackend(FormatterBackend): Args: model_id (str | ModelIdentifier): Ollama model ID. If a - ``ModelIdentifier`` is passed, its ``ollama_name`` attribute must + `ModelIdentifier` is passed, its `ollama_name` attribute must be set. formatter (ChatFormatter | None): Formatter for rendering components. - Defaults to ``TemplateFormatter``. + Defaults to `TemplateFormatter`. base_url (str | None): Ollama server endpoint; defaults to - ``env(OLLAMA_HOST)`` or ``http://localhost:11434``. + `env(OLLAMA_HOST)` or `http://localhost:11434`. model_options (dict | None): Default model options for generation requests. timeout (float | None): Request timeout in seconds for the underlying HTTP - client. ``None`` (the default) preserves the upstream ``ollama`` SDK + client. `None` (the default) preserves the upstream `ollama` SDK default. Set this to bound how long a single request will wait when the Ollama server is overloaded or stalled. Attributes: to_mellea_model_opts_map (dict): Mapping from Ollama-specific option names - to Mellea ``ModelOption`` sentinel keys. - from_mellea_model_opts_map (dict): Mapping from Mellea ``ModelOption`` + to Mellea `ModelOption` sentinel keys. + from_mellea_model_opts_map (dict): Mapping from Mellea `ModelOption` sentinel keys to Ollama-specific option names. """ @@ -281,9 +281,9 @@ async def _generate_from_context( model_options: dict | None = None, tool_calls: bool = False, ) -> tuple[ModelOutputThunk[C], Context]: - """Generate a completion for ``action`` given ``ctx`` via the Ollama chat API. + """Generate a completion for `action` given `ctx` via the Ollama chat API. - Delegates to ``generate_from_chat_context``. Only chat contexts are supported. + Delegates to `generate_from_chat_context`. Only chat contexts are supported. Args: action (Component[C] | CBlock): The component or content block to generate @@ -293,12 +293,12 @@ async def _generate_from_context( structured/constrained output decoding. model_options (dict | None): Per-call model options that override the backend's defaults. - tool_calls (bool): If ``True``, expose available tools to the model and + tool_calls (bool): If `True`, expose available tools to the model and parse tool-call responses. Returns: tuple[ModelOutputThunk[C], Context]: A thunk holding the (lazy) model output - and an updated context that includes ``action`` and the new output. + and an updated context that includes `action` and the new output. """ # Start span without auto-closing (will be closed in post_processing) span = start_generate_span(self, action, ctx, format, tool_calls) @@ -334,7 +334,7 @@ async def generate_from_chat_context( ) -> ModelOutputThunk[C]: """Generate a new completion from the provided context using this backend's formatter. - Treats the ``Context`` as a chat history and uses the ``ollama.Client.chat()`` + Treats the `Context` as a chat history and uses the `ollama.Client.chat()` interface to generate a completion. Returns a thunk that lazily resolves the model output. @@ -345,7 +345,7 @@ async def generate_from_chat_context( _format (type[BaseModelSubclass] | None): Optional Pydantic model class for structured output decoding. model_options (dict | None): Per-call model options. - tool_calls (bool): If ``True``, expose available tools and parse responses. + tool_calls (bool): If `True`, expose available tools and parse responses. Returns: ModelOutputThunk[C]: A thunk holding the (lazy) model output. @@ -629,9 +629,9 @@ async def processing( ): """Accumulate text and tool calls from a single Ollama ChatResponse chunk. - Called for each streaming or non-streaming ``ollama.ChatResponse``. Also + Called for each streaming or non-streaming `ollama.ChatResponse`. Also extracts tool call requests inline and merges the chunk into the running - aggregated response stored in ``mot._meta["chat_response"]``. + aggregated response stored in `mot._meta["chat_response"]`. Args: mot (ModelOutputThunk): The output thunk being populated. diff --git a/mellea/backends/openai.py b/mellea/backends/openai.py index 59f961d1f..81b2864db 100644 --- a/mellea/backends/openai.py +++ b/mellea/backends/openai.py @@ -77,32 +77,32 @@ class OpenAIBackend(FormatterBackend, AdapterMixin): Args: model_id (str | ModelIdentifier): OpenAI-compatible model identifier. - Defaults to ``model_ids.OPENAI_GPT_5_1``. + Defaults to `model_ids.OPENAI_GPT_5_1`. formatter (ChatFormatter | None): Formatter for rendering components. - Defaults to ``TemplateFormatter``. + Defaults to `TemplateFormatter`. base_url (str | None): Base URL for the API endpoint; defaults to the standard OpenAI endpoint if not set. model_options (dict | None): Default model options for generation requests. - default_to_constraint_checking_alora (bool): If ``False``, deactivates aLoRA + default_to_constraint_checking_alora (bool): If `False`, deactivates aLoRA constraint checking; primarily for benchmarking and debugging. - load_embedded_adapters (bool): If ``True``, automatically registers + load_embedded_adapters (bool): If `True`, automatically registers embedded intrinsic adapters from *adapter_source* (or *model_id* if *adapter_source* is not set). Looks first for a local directory and then for a HuggingFace hub repo. adapter_source (str | None): Local directory path or HuggingFace hub - repo ID from which to load embedded adapter configs. When ``None``, + repo ID from which to load embedded adapter configs. When `None`, falls back to *model_id*. Use this when the vLLM served model name differs from the adapter config location. - api_key (str | None): API key; falls back to ``OPENAI_API_KEY`` env var. + api_key (str | None): API key; falls back to `OPENAI_API_KEY` env var. kwargs: Additional keyword arguments forwarded to the OpenAI client. Attributes: to_mellea_model_opts_map_chats (dict): Mapping from chat-endpoint option names - to Mellea ``ModelOption`` sentinel keys. + to Mellea `ModelOption` sentinel keys. from_mellea_model_opts_map_chats (dict): Mapping from Mellea sentinel keys to chat-endpoint option names. to_mellea_model_opts_map_completions (dict): Mapping from completions-endpoint - option names to Mellea ``ModelOption`` sentinel keys. + option names to Mellea `ModelOption` sentinel keys. from_mellea_model_opts_map_completions (dict): Mapping from Mellea sentinel keys to completions-endpoint option names. """ @@ -250,7 +250,7 @@ def add_adapter(self, adapter: Adapter) -> None: adapter: The adapter to register. Raises: - TypeError: If *adapter* is not an ``EmbeddedIntrinsicAdapter``. + TypeError: If *adapter* is not an `EmbeddedIntrinsicAdapter`. """ if not isinstance(adapter, EmbeddedIntrinsicAdapter): raise TypeError( @@ -333,7 +333,7 @@ def filter_openai_client_kwargs(**kwargs) -> dict: kwargs: Arbitrary keyword arguments to filter. Returns: - dict: A dict containing only keys accepted by ``openai.OpenAI.__init__``. + dict: A dict containing only keys accepted by `openai.OpenAI.__init__`. """ openai_params = set(inspect.signature(openai.OpenAI.__init__).parameters.keys()) # type: ignore openai_params.discard("self") # Remove 'self' parameter @@ -349,7 +349,7 @@ def filter_chat_completions_kwargs(self, model_options: dict) -> dict: model_options (dict): Model options dict that may contain non-chat keys. Returns: - dict: A dict containing only keys accepted by ``chat.completions.create``. + dict: A dict containing only keys accepted by `chat.completions.create`. """ from openai.resources.chat.completions import Completions @@ -367,7 +367,7 @@ def filter_completions_kwargs(self, model_options: dict) -> dict: model_options (dict): Model options dict that may contain non-completions keys. Returns: - dict: A dict containing only keys accepted by ``completions.create``. + dict: A dict containing only keys accepted by `completions.create`. """ from openai.resources.completions import Completions @@ -446,9 +446,9 @@ async def _generate_from_context( model_options: dict | None = None, tool_calls: bool = False, ) -> tuple[ModelOutputThunk[C], Context]: - """Generate a completion for ``action`` given ``ctx`` via the OpenAI chat API. + """Generate a completion for `action` given `ctx` via the OpenAI chat API. - Delegates to ``generate_from_chat_context``. Only chat contexts are supported. + Delegates to `generate_from_chat_context`. Only chat contexts are supported. Args: action (Component[C] | CBlock): The component or content block to generate @@ -458,12 +458,12 @@ async def _generate_from_context( structured/constrained output decoding. model_options (dict | None): Per-call model options that override the backend's defaults. - tool_calls (bool): If ``True``, expose available tools to the model and + tool_calls (bool): If `True`, expose available tools to the model and parse tool-call responses. Returns: tuple[ModelOutputThunk[C], Context]: A thunk holding the (lazy) model output - and an updated context that includes ``action`` and the new output. + and an updated context that includes `action` and the new output. """ assert ctx.is_chat_context, NotImplementedError( "The Openai backend only supports chat-like contexts." @@ -556,7 +556,7 @@ async def _generate_from_intrinsic( """Generate a completion for an intrinsic action using an embedded adapter. Applies the intrinsic's I/O rewriter to transform the conversation, - injects ``intrinsic_name`` into ``chat_template_kwargs`` so that the + injects `intrinsic_name` into `chat_template_kwargs` so that the Granite Switch chat template activates the correct adapter, and post-processes the model output through the intrinsic's result processor. @@ -569,7 +569,7 @@ async def _generate_from_intrinsic( action (Intrinsic): The intrinsic component to execute. ctx (Context): The current generation context (must be a chat context). model_options (dict[str, Any]): Merged model options for this call. - tool_calls (bool): If ``True``, expose available tools to the model + tool_calls (bool): If `True`, expose available tools to the model and parse tool-call responses. Returns: @@ -788,7 +788,7 @@ async def generate_from_chat_context( model_options: dict | None = None, tool_calls: bool = False, ) -> tuple[ModelOutputThunk[C], Context]: - """Generate a new completion from the provided Context using this backend's ``Formatter``. + """Generate a new completion from the provided Context using this backend's `Formatter`. Formats the context and action into OpenAI-compatible chat messages, submits the request asynchronously, and returns a thunk that lazily resolves the output. @@ -800,11 +800,11 @@ async def generate_from_chat_context( _format (type[BaseModelSubclass] | None): Optional Pydantic model class for structured output decoding. model_options (dict | None): Per-call model options. - tool_calls (bool): If ``True``, expose available tools and parse responses. + tool_calls (bool): If `True`, expose available tools and parse responses. Returns: tuple[ModelOutputThunk[C], Context]: A thunk holding the (lazy) model output - and an updated context that includes ``action`` and the new output. + and an updated context that includes `action` and the new output. """ await self.do_generate_walk(action) @@ -975,8 +975,8 @@ async def processing( ): """Accumulate content from a single OpenAI response object into the output thunk. - Called for each ``ChatCompletion`` (non-streaming) or ``ChatCompletionChunk`` - (streaming). Tool call parsing is deferred to ``post_processing``. + Called for each `ChatCompletion` (non-streaming) or `ChatCompletionChunk` + (streaming). Tool call parsing is deferred to `post_processing`. Args: mot (ModelOutputThunk): The output thunk being populated. @@ -1054,9 +1054,9 @@ async def post_processing( tools (dict[str, AbstractMelleaTool]): Available tools, keyed by name. conversation (list[dict]): The chat conversation sent to the model, used for logging. - thinking: The reasoning effort level passed to the model, or ``None`` + thinking: The reasoning effort level passed to the model, or `None` if reasoning mode was not enabled. - seed: The random seed used during generation, or ``None``. + seed: The random seed used during generation, or `None`. _format: The structured output format class used during generation, if any. """ # Reconstruct the chat_response from chunks if streamed. diff --git a/mellea/backends/tools.py b/mellea/backends/tools.py index c81a59b6f..e1dddef14 100644 --- a/mellea/backends/tools.py +++ b/mellea/backends/tools.py @@ -1,6 +1,6 @@ """LLM tool definitions, parsing, and validation for mellea backends. -Provides the ``MelleaTool`` class (and the ``@tool`` decorator shorthand) for +Provides the `MelleaTool` class (and the `@tool` decorator shorthand) for wrapping Python callables as OpenAI-compatible tool schemas, with factory methods for LangChain and smolagents interoperability. Also includes helpers for converting tool lists to JSON, extracting tool call requests from raw LLM output strings, and @@ -83,14 +83,14 @@ def from_langchain(cls, tool: Any) -> "MelleaTool[..., Any]": """Create a MelleaTool from a LangChain tool object. Args: - tool (Any): A ``langchain_core.tools.BaseTool`` instance to wrap. + tool (Any): A `langchain_core.tools.BaseTool` instance to wrap. Returns: MelleaTool[..., Any]: A Mellea tool wrapping the LangChain tool. Raises: - ImportError: If ``langchain-core`` is not installed. - ValueError: If ``tool`` is not a ``BaseTool`` instance. + ImportError: If `langchain-core` is not installed. + ValueError: If `tool` is not a `BaseTool` instance. """ try: from langchain_core.tools import BaseTool # type: ignore[import-not-found] @@ -200,14 +200,14 @@ def from_callable( Introspects the callable's signature and docstring to build an OpenAI-compatible JSON schema automatically. Async functions (defined - with ``async def``) are supported transparently: the coroutine is - awaited on mellea's shared event loop so sync callers of ``.run()`` + with `async def`) are supported transparently: the coroutine is + awaited on mellea's shared event loop so sync callers of `.run()` receive the resolved value rather than an un-awaited coroutine. Args: func (Callable[P, R] | Callable[P, Awaitable[R]]): The Python callable (sync or async) to wrap as a tool. - name (str | None): Optional name override; defaults to ``func.__name__``. + name (str | None): Optional name override; defaults to `func.__name__`. Returns: MelleaTool[P, R]: A Mellea tool wrapping the callable with preserved parameter and return types. @@ -320,8 +320,8 @@ def add_tools_from_model_options( Args: tools_dict: Mutable mapping of tool name to tool instance; modified in-place. - model_options: Model options dict that may contain a ``ModelOption.TOOLS`` - entry (either a list of ``MelleaTool`` or a ``dict[str, MelleaTool]``). + model_options: Model options dict that may contain a `ModelOption.TOOLS` + entry (either a list of `MelleaTool` or a `dict[str, MelleaTool]`). """ model_opts_tools = model_options.get(ModelOption.TOOLS, None) if model_opts_tools is None: @@ -363,8 +363,8 @@ def add_tools_from_context_actions( Args: tools_dict: Mutable mapping of tool name to tool instance; modified in-place. - ctx_actions: List of ``Component`` or ``CBlock`` objects whose template - representations may declare tools, or ``None`` to skip. + ctx_actions: List of `Component` or `CBlock` objects whose template + representations may declare tools, or `None` to skip. """ if ctx_actions is None: return @@ -385,7 +385,7 @@ def convert_tools_to_json(tools: dict[str, AbstractMelleaTool]) -> list[dict]: """Convert tools to json dict representation. Args: - tools: Mapping of tool name to ``AbstractMelleaTool`` instance. + tools: Mapping of tool name to `AbstractMelleaTool` instance. Returns: List of OpenAI-compatible JSON tool schema dicts, one per tool. @@ -405,7 +405,7 @@ def json_extraction(text: str) -> Generator[dict, None, None]: text: Input string potentially containing one or more JSON objects. Yields: - Each valid JSON object found in ``text``, in order of appearance. + Each valid JSON object found in `text`, in order of appearance. """ index = 0 decoder = json.JSONDecoder() @@ -428,15 +428,15 @@ def json_extraction(text: str) -> Generator[dict, None, None]: def find_func(d: object) -> tuple[str | None, Mapping | None]: """Find the first function in a json-like dictionary. - Most llms output tool requests in the form ``...{"name": string, "arguments": {}}...`` + Most llms output tool requests in the form `...{"name": string, "arguments": {}}...` Args: - d: A JSON-like Python object (typically a ``dict``) to search for a function + d: A JSON-like Python object (typically a `dict`) to search for a function call record. Returns: - A ``(name, args)`` tuple where ``name`` is the tool name string and ``args`` - is the arguments mapping, or ``(None, None)`` if no function call was found. + A `(name, args)` tuple where `name` is the tool name string and `args` + is the arguments mapping, or `(None, None)` if no function call was found. """ if not isinstance(d, dict): return None, None @@ -468,7 +468,7 @@ def parse_tools(llm_response: str) -> list[tuple[str, Mapping]]: llm_response: Raw string output from a language model. Returns: - List of ``(tool_name, arguments)`` tuples for each tool call found. + List of `(tool_name, arguments)` tuples for each tool call found. """ processed = " ".join(llm_response.split()) @@ -740,10 +740,10 @@ def _build_pydantic_type_from_schema(schema: dict[str, Any]) -> Any: # so that all backends don't need it installed. # https://github.com/ollama/ollama-python/blob/60e7b2f9ce710eeb57ef2986c46ea612ae7516af/ollama/_types.py#L19-L101 class SubscriptableBaseModel(BaseModel): - """Pydantic ``BaseModel`` subclass that also supports subscript (``[]``) access. + """Pydantic `BaseModel` subclass that also supports subscript (`[]`) access. Imported from the Ollama Python client. Allows model fields to be accessed - via ``model["field"]`` in addition to ``model.field``, which is required for + via `model["field"]` in addition to `model.field`, which is required for compatibility with Ollama's internal response parsing. """ @@ -822,11 +822,11 @@ def get(self, key: str, default: Any = None) -> Any: Args: key (str): The field name to look up on the model. - default (Any): Value to return when ``key`` is not a field on the model. - Defaults to ``None``. + default (Any): Value to return when `key` is not a field on the model. + Defaults to `None`. Returns: - Any: The field value if the attribute exists, otherwise ``default``. + Any: The field value if the attribute exists, otherwise `default`. >>> msg = Message(role='user') >>> msg.get('role') @@ -849,10 +849,10 @@ class OllamaTool(SubscriptableBaseModel): Represents the JSON structure that Ollama (and OpenAI-compatible endpoints) expect when a tool is passed to the chat API. Mellea builds these objects internally via - ``convert_function_to_ollama_tool`` and never exposes them to end users directly. + `convert_function_to_ollama_tool` and never exposes them to end users directly. Attributes: - type (str | None): Tool type; always ``"function"`` for function-calling tools. + type (str | None): Tool type; always `"function"` for function-calling tools. function (Function | None): Nested object containing the function name, description, and parameters schema. """ @@ -860,7 +860,7 @@ class OllamaTool(SubscriptableBaseModel): type: str | None = "function" class Function(SubscriptableBaseModel): - """Pydantic model for the ``function`` field of an Ollama tool schema, imported from the Ollama Python SDK. + """Pydantic model for the `function` field of an Ollama tool schema, imported from the Ollama Python SDK. Attributes: name (str | None): The name of the function being described. @@ -872,11 +872,11 @@ class Function(SubscriptableBaseModel): description: str | None = None class Parameters(SubscriptableBaseModel): - """Pydantic model for the ``parameters`` field of an Ollama function schema, imported from the Ollama Python SDK. + """Pydantic model for the `parameters` field of an Ollama function schema, imported from the Ollama Python SDK. Attributes: - type (Literal["object"] | None): Always ``"object"`` for function parameters. - defs (Any | None): JSON Schema ``$defs`` for referenced sub-schemas. + type (Literal["object"] | None): Always `"object"` for function parameters. + defs (Any | None): JSON Schema `$defs` for referenced sub-schemas. items (Any | None): Array item schema, if applicable. required (Sequence[str] | None): List of required parameter names. properties (Mapping[str, Property] | None): Parameter property definitions. @@ -983,7 +983,7 @@ def _is_complex_anyof(v: dict) -> bool: continue # Check for references, nested properties, or oneOf branches # (don't recursively check allOf). oneOf appears in Pydantic discriminated - # unions: ``Annotated[A | B, Field(discriminator=...)] | None``. + # unions: `Annotated[A | B, Field(discriminator=...)] | None`. if "$ref" in sub_schema or "properties" in sub_schema or "oneOf" in sub_schema: return True return False @@ -992,25 +992,25 @@ def _is_complex_anyof(v: dict) -> bool: def _flatten_discriminated_union(v: dict, defs: dict) -> dict: """Normalise Pydantic discriminated-union schemas for tool-calling APIs. - Pydantic emits ``Annotated[A | B, Field(discriminator="kind")]`` as either: + Pydantic emits `Annotated[A | B, Field(discriminator="kind")]` as either: - - Required: ``{"discriminator": {...}, "oneOf": [{"$ref": ...}, ...]}`` - - Optional: ``{"anyOf": [{"discriminator": {...}, "oneOf": [...]}, {"type": "null"}]}`` + - Required: `{"discriminator": {...}, "oneOf": [{"$ref": ...}, ...]}` + - Optional: `{"anyOf": [{"discriminator": {...}, "oneOf": [...]}, {"type": "null"}]}` - The OAS-3 ``discriminator`` keyword and the JSON Schema ``oneOf`` keyword + The OAS-3 `discriminator` keyword and the JSON Schema `oneOf` keyword both fall outside the schema subset accepted by tool-calling APIs (Ollama, - OpenAI strict mode). The ``Literal`` constraint on the tag field carries the - discriminator signal, so we can safely drop ``discriminator`` and emit - ``anyOf`` — equivalent here because the tag field forces uniqueness. + OpenAI strict mode). The `Literal` constraint on the tag field carries the + discriminator signal, so we can safely drop `discriminator` and emit + `anyOf` — equivalent here because the tag field forces uniqueness. - Returns a new schema dict with ``oneOf`` rewritten to ``anyOf``, branches - inlined, and ``discriminator`` stripped. The input is not mutated; callers + Returns a new schema dict with `oneOf` rewritten to `anyOf`, branches + inlined, and `discriminator` stripped. The input is not mutated; callers must reassign the result. Flattening is single-level. Discriminated unions nested inside an inlined branch (e.g. a Pydantic model whose own field is another discriminated union) are not recursively flattened — that case is tracked alongside the - recursive ``$ref`` resolution work in #911. + recursive `$ref` resolution work in #911. """ def _inline(branch: dict) -> dict: @@ -1023,8 +1023,8 @@ def _inline(branch: dict) -> dict: out = {kk: vv for kk, vv in v.items() if kk not in ("oneOf", "discriminator")} # Top-level discriminated union (required parameter case). Append rather - # than overwrite so a defensive ``oneOf`` + ``anyOf`` co-occurrence does - # not silently drop the existing ``anyOf`` entries. Pydantic does not + # than overwrite so a defensive `oneOf` + `anyOf` co-occurrence does + # not silently drop the existing `anyOf` entries. Pydantic does not # currently emit both at the same level, but the helper is callable in # isolation and should not lose data. if "oneOf" in v: @@ -1055,10 +1055,10 @@ def convert_function_to_ollama_tool( Args: func: The Python callable to convert. - name: Optional override for the tool name; defaults to ``func.__name__``. + name: Optional override for the tool name; defaults to `func.__name__`. Returns: - An ``OllamaTool`` instance representing the function as an OpenAI-compatible + An `OllamaTool` instance representing the function as an OpenAI-compatible tool schema. """ doc_string_hash = str(hash(inspect.getdoc(func))) diff --git a/mellea/backends/utils.py b/mellea/backends/utils.py index 6709cda89..e52d98fb1 100644 --- a/mellea/backends/utils.py +++ b/mellea/backends/utils.py @@ -1,10 +1,10 @@ """Shared utility functions used across formatter-based backend implementations. -Provides ``to_chat``, which converts a ``Context`` and a ``Component`` action into -the list of role/content dicts expected by ``apply_chat_template``; and -``to_tool_calls``, which parses a raw model output string into validated -``ModelToolCall`` objects. These helpers are consumed internally by all -``FormatterBackend`` subclasses. +Provides `to_chat`, which converts a `Context` and a `Component` action into +the list of role/content dicts expected by `apply_chat_template`; and +`to_tool_calls`, which parses a raw model output string into validated +`ModelToolCall` objects. These helpers are consumed internally by all +`FormatterBackend` subclasses. """ from __future__ import annotations @@ -56,7 +56,7 @@ def to_chat( system_prompt: Optional system prompt to prepend; overrides any system message in the context. Returns: - List of role/content dicts suitable for ``apply_chat_template``. + List of role/content dicts suitable for `apply_chat_template`. """ assert ctx.is_chat_context @@ -96,11 +96,11 @@ def to_tool_calls( """Parse a tool call string. Args: - tools: Mapping of tool name to the corresponding ``AbstractMelleaTool`` object. + tools: Mapping of tool name to the corresponding `AbstractMelleaTool` object. decoded_result: Raw model output string that may contain tool call markup. Returns: - Dict mapping tool name to validated ``ModelToolCall``, or ``None`` if no tool calls were found. + Dict mapping tool name to validated `ModelToolCall`, or `None` if no tool calls were found. """ model_tool_calls: dict[str, ModelToolCall] = dict() for tool_name, tool_args in parse_tools(decoded_result): diff --git a/mellea/backends/watsonx.py b/mellea/backends/watsonx.py index 8c098c995..470db19b7 100644 --- a/mellea/backends/watsonx.py +++ b/mellea/backends/watsonx.py @@ -66,24 +66,24 @@ class WatsonxAIBackend(FormatterBackend): Args: model_id (str | ModelIdentifier): WatsonX model identifier. Defaults to - ``model_ids.IBM_GRANITE_4_HYBRID_SMALL``. + `model_ids.IBM_GRANITE_4_HYBRID_SMALL`. formatter (ChatFormatter | None): Formatter for rendering components. - Defaults to ``TemplateFormatter``. + Defaults to `TemplateFormatter`. base_url (str | None): URL for the WatsonX ML deployment endpoint; - defaults to the ``WATSONX_URL`` environment variable. + defaults to the `WATSONX_URL` environment variable. model_options (dict | None): Default model options for generation requests. api_key (str | None): WatsonX API key; defaults to the - ``WATSONX_API_KEY`` environment variable. + `WATSONX_API_KEY` environment variable. project_id (str | None): WatsonX project ID; defaults to the - ``WATSONX_PROJECT_ID`` environment variable. + `WATSONX_PROJECT_ID` environment variable. Attributes: to_mellea_model_opts_map_chats (dict): Mapping from chat-endpoint option names - to Mellea ``ModelOption`` sentinel keys. + to Mellea `ModelOption` sentinel keys. from_mellea_model_opts_map_chats (dict): Mapping from Mellea sentinel keys to chat-endpoint option names. to_mellea_model_opts_map_completions (dict): Mapping from completions-endpoint - option names to Mellea ``ModelOption`` sentinel keys. + option names to Mellea `ModelOption` sentinel keys. from_mellea_model_opts_map_completions (dict): Mapping from Mellea sentinel keys to completions-endpoint option names. """ @@ -284,10 +284,10 @@ async def _generate_from_context( model_options: dict | None = None, tool_calls: bool = False, ) -> tuple[ModelOutputThunk[C], Context]: - """Generate a completion for ``action`` given ``ctx`` via the WatsonX chat API. + """Generate a completion for `action` given `ctx` via the WatsonX chat API. - Delegates to ``generate_from_chat_context``. Only chat contexts are - supported; raises ``NotImplementedError`` otherwise. + Delegates to `generate_from_chat_context`. Only chat contexts are + supported; raises `NotImplementedError` otherwise. Args: action (Component[C] | CBlock): The component or content block to generate @@ -297,12 +297,12 @@ async def _generate_from_context( structured/constrained output decoding. model_options (dict | None): Per-call model options that override the backend's defaults. - tool_calls (bool): If ``True``, expose available tools to the model and + tool_calls (bool): If `True`, expose available tools to the model and parse tool-call responses. Returns: tuple[ModelOutputThunk[C], Context]: A thunk holding the (lazy) model output - and an updated context that includes ``action`` and the new output. + and an updated context that includes `action` and the new output. """ assert ctx.is_chat_context, NotImplementedError( "The watsonx.ai backend only supports chat-like contexts." @@ -349,13 +349,13 @@ async def generate_from_chat_context( _format (type[BaseModelSubclass] | None): Optional Pydantic model class for structured output decoding. model_options (dict | None): Per-call model options. - tool_calls (bool): If ``True``, expose available tools and parse responses. + tool_calls (bool): If `True`, expose available tools and parse responses. Returns: ModelOutputThunk[C]: A thunk holding the (lazy) model output. Raises: - Exception: If ``action`` is an ``ALoraRequirement``, which is not + Exception: If `action` is an `ALoraRequirement`, which is not supported by this backend. RuntimeError: If not called from a thread with a running event loop. """ @@ -489,8 +489,8 @@ async def generate_from_chat_context( async def processing(self, mot: ModelOutputThunk, chunk: dict): """Accumulate content from a single WatsonX response dict into the output thunk. - Called for each non-streaming chat dict (with a ``"message"`` key) or - streaming delta dict (with a ``"delta"`` key). Tool call parsing is + Called for each non-streaming chat dict (with a `"message"` key) or + streaming delta dict (with a `"delta"` key). Tool call parsing is handled in the post-processing step. Args: @@ -557,7 +557,7 @@ async def post_processing( conversation (list[dict]): The chat conversation sent to the model, used for logging. tools (dict[str, AbstractMelleaTool]): Available tools, keyed by name. - seed: The random seed used during generation, or ``None``. + seed: The random seed used during generation, or `None`. _format: The structured output format class used during generation, if any. """ # Reconstruct the chat_response from chunks if streamed. @@ -678,7 +678,7 @@ async def generate_from_raw( """Generate completions for multiple actions without chat templating via WatsonX. Passes formatted prompt strings directly to WatsonX's generate endpoint. - The ``format`` parameter is not supported and will be ignored with a warning. + The `format` parameter is not supported and will be ignored with a warning. Args: actions (Sequence[Component[C] | CBlock]): Actions to generate completions for. diff --git a/mellea/core/__init__.py b/mellea/core/__init__.py index 805e8a63f..0f417ae1a 100644 --- a/mellea/core/__init__.py +++ b/mellea/core/__init__.py @@ -1,10 +1,10 @@ """Core abstractions for the mellea library. This package defines the fundamental interfaces and data structures on which every -other layer of mellea is built: the ``Backend``, ``Formatter``, and -``SamplingStrategy`` protocols; the ``Component``, ``CBlock``, ``Context``, and -``ModelOutputThunk`` data types that flow through the inference pipeline; and -``Requirement`` / ``ValidationResult`` / ``PartialValidationResult`` for constrained generation. Start here when +other layer of mellea is built: the `Backend`, `Formatter`, and +`SamplingStrategy` protocols; the `Component`, `CBlock`, `Context`, and +`ModelOutputThunk` data types that flow through the inference pipeline; and +`Requirement` / `ValidationResult` / `PartialValidationResult` for constrained generation. Start here when building a new backend, formatter, or sampling strategy, or when you need the type definitions shared across the library. """ diff --git a/mellea/core/backend.py b/mellea/core/backend.py index a1ea18f13..444c75432 100644 --- a/mellea/core/backend.py +++ b/mellea/core/backend.py @@ -1,10 +1,10 @@ -"""Abstract ``Backend`` interface and generation-walk utilities. +"""Abstract `Backend` interface and generation-walk utilities. -Defines the ``Backend`` abstract base class whose two key abstract methods — -``generate_from_context`` (context-aware single-action generation) and -``generate_from_raw`` (context-free batch generation) — all concrete backends must -implement. Also provides ``generate_walk``, which traverses a ``Component`` tree to -find un-computed ``ModelOutputThunk`` leaves that need to be resolved before rendering. +Defines the `Backend` abstract base class whose two key abstract methods — +`generate_from_context` (context-aware single-action generation) and +`generate_from_raw` (context-free batch generation) — all concrete backends must +implement. Also provides `generate_walk`, which traverses a `Component` tree to +find un-computed `ModelOutputThunk` leaves that need to be resolved before rendering. """ import abc @@ -42,10 +42,10 @@ class Backend(abc.ABC): """Abstract base class for all inference backends. - All concrete backends must implement ``generate_from_context`` (context-aware - single-action generation) and ``generate_from_raw`` (context-free batch - generation). The ``do_generate_walk`` / ``do_generate_walks`` helpers can be - used to pre-compute any unresolved ``ModelOutputThunk`` leaves before rendering. + All concrete backends must implement `generate_from_context` (context-aware + single-action generation) and `generate_from_raw` (context-free batch + generation). The `do_generate_walk` / `do_generate_walks` helpers can be + used to pre-compute any unresolved `ModelOutputThunk` leaves before rendering. """ @final @@ -162,16 +162,16 @@ async def generate_from_raw( tool_calls: Always set to false unless supported by backend. Returns: - list[ModelOutputThunk]: A list of output thunks, one per action, in the same order as ``actions``. + list[ModelOutputThunk]: A list of output thunks, one per action, in the same order as `actions`. """ async def do_generate_walk( self, action: CBlock | Component | ModelOutputThunk ) -> None: - """Awaits all uncomputed ``ModelOutputThunk`` leaves reachable from ``action``. + """Awaits all uncomputed `ModelOutputThunk` leaves reachable from `action`. - Traverses the component tree rooted at ``action`` via ``generate_walk``, collects - any uncomputed ``ModelOutputThunk`` nodes, and concurrently awaits them all. + Traverses the component tree rooted at `action` via `generate_walk`, collects + any uncomputed `ModelOutputThunk` nodes, and concurrently awaits them all. Args: action (CBlock | Component | ModelOutputThunk): The root node to traverse. @@ -188,10 +188,10 @@ async def do_generate_walk( async def do_generate_walks( self, actions: list[CBlock | Component | ModelOutputThunk] ) -> None: - """Awaits all uncomputed ``ModelOutputThunk`` leaves reachable from each action in ``actions``. + """Awaits all uncomputed `ModelOutputThunk` leaves reachable from each action in `actions`. - Traverses the component tree of every action in the list via ``generate_walk``, collects - all uncomputed ``ModelOutputThunk`` nodes across all actions, and concurrently awaits them. + Traverses the component tree of every action in the list via `generate_walk`, collects + all uncomputed `ModelOutputThunk` nodes across all actions, and concurrently awaits them. Args: actions (list[CBlock | Component | ModelOutputThunk]): The list of root nodes to traverse. @@ -209,18 +209,18 @@ async def do_generate_walks( def generate_walk(c: CBlock | Component | ModelOutputThunk) -> list[ModelOutputThunk]: - """Return all uncomputed ``ModelOutputThunk`` leaves reachable from ``c``. + """Return all uncomputed `ModelOutputThunk` leaves reachable from `c`. Args: - c: A ``CBlock``, ``Component``, or ``ModelOutputThunk`` to traverse. + c: A `CBlock`, `Component`, or `ModelOutputThunk` to traverse. Returns: - A flat list of uncomputed ``ModelOutputThunk`` instances in the order - they need to be resolved (depth-first over ``Component.parts()``). + A flat list of uncomputed `ModelOutputThunk` instances in the order + they need to be resolved (depth-first over `Component.parts()`). Raises: - ValueError: If any element encountered during traversal is not a ``CBlock``, - ``Component``, or ``ModelOutputThunk``. + ValueError: If any element encountered during traversal is not a `CBlock`, + `Component`, or `ModelOutputThunk`. """ match c: case ModelOutputThunk() if not c.is_computed(): diff --git a/mellea/core/base.py b/mellea/core/base.py index dbaf15f9d..65ed6bd35 100644 --- a/mellea/core/base.py +++ b/mellea/core/base.py @@ -1,11 +1,11 @@ """Foundational data structures for mellea's generative programming model. -Defines the building blocks that flow through every layer of the library: ``CBlock`` -(a content block wrapping a string value), ``Component`` (an abstract composable -generative unit), ``ModelOutputThunk`` (a lazily-evaluated model response), -``Context`` and ``ContextTurn`` (stateful conversation history containers), -``TemplateRepresentation`` (the structured rendering of a component for prompt -templates), ``ImageBlock``, and ``ModelToolCall``. Understanding these types is +Defines the building blocks that flow through every layer of the library: `CBlock` +(a content block wrapping a string value), `Component` (an abstract composable +generative unit), `ModelOutputThunk` (a lazily-evaluated model response), +`Context` and `ContextTurn` (stateful conversation history containers), +`TemplateRepresentation` (the structured rendering of a component for prompt +templates), `ImageBlock`, and `ModelToolCall`. Understanding these types is the starting point for building custom components or sampling strategies. """ @@ -46,7 +46,7 @@ class CBlock: value (str | None): The underlying string content of the block. meta (dict[str, Any] | None): Optional metadata about this block (e.g., the inference engine's completion object). Defaults to an empty dict. - cache (bool): If ``True``, the inference engine may store the KV cache for this block. Experimental. + cache (bool): If `True`, the inference engine may store the KV cache for this block. Experimental. """ @@ -98,7 +98,7 @@ def __init__(self, value: str, meta: dict[str, Any] | None = None): """Initialize ImageBlock with a base64-encoded PNG string, validating the encoding. Raises: - AssertionError: If ``value`` is not a valid base64-encoded PNG string. + AssertionError: If `value` is not a valid base64-encoded PNG string. """ assert self.is_valid_base64_png(value), ( "Invalid base64 string representation of image." @@ -116,7 +116,7 @@ def is_valid_base64_png(s: str) -> bool: s (str): The string to validate, optionally prefixed with a data URI header. Returns: - bool: ``True`` if the string decodes to a PNG image, ``False`` otherwise. + bool: `True` if the string decodes to a PNG image, `False` otherwise. """ try: # Check if the string has a data URI prefix and remove it. @@ -162,17 +162,17 @@ def pil_to_base64(image: PILImage.Image) -> str: def from_pil_image( cls, image: PILImage.Image, meta: dict[str, Any] | None = None ) -> ImageBlock: - """Creates an ``ImageBlock`` from a PIL image object. + """Creates an `ImageBlock` from a PIL image object. Converts the image to a base64-encoded PNG string and wraps it in a new - ``ImageBlock`` instance. + `ImageBlock` instance. Args: image (PILImage.Image): The PIL image to encode. meta (dict[str, Any] | None): Optional metadata to associate with the block. Returns: - ImageBlock: A new ``ImageBlock`` containing the base64-encoded PNG. + ImageBlock: A new `ImageBlock` containing the base64-encoded PNG. """ image_base64 = cls.pil_to_base64(image) return cls(image_base64, meta) @@ -198,10 +198,10 @@ class Component(Protocol, Generic[S]): """A `Component` is a composite data structure that is intended to be represented to an LLM.""" def parts(self) -> list[Component | CBlock]: - """Returns the set of all constituent sub-components and content blocks of this ``Component``. + """Returns the set of all constituent sub-components and content blocks of this `Component`. Returns: - list[Component | CBlock]: A list of child ``Component`` or ``CBlock`` objects that make + list[Component | CBlock]: A list of child `Component` or `CBlock` objects that make up this component. The list may be empty for leaf components. Raises: @@ -210,10 +210,10 @@ def parts(self) -> list[Component | CBlock]: raise NotImplementedError("parts isn't implemented by default") def format_for_llm(self) -> TemplateRepresentation | str: - """Formats the ``Component`` into a ``TemplateRepresentation`` or plain string for LLM consumption. + """Formats the `Component` into a `TemplateRepresentation` or plain string for LLM consumption. Returns: - TemplateRepresentation | str: A structured ``TemplateRepresentation`` (for components + TemplateRepresentation | str: A structured `TemplateRepresentation` (for components with tools, fields, or templates) or a plain string for simple components. Raises: @@ -222,19 +222,19 @@ def format_for_llm(self) -> TemplateRepresentation | str: raise NotImplementedError("format_for_llm isn't implemented by default") def parse(self, computed: ModelOutputThunk) -> S: - """Parses the expected type ``S`` from a given ``ModelOutputThunk``. + """Parses the expected type `S` from a given `ModelOutputThunk`. - Delegates to the component's underlying ``_parse`` method and wraps any - exception in a ``ComponentParseError`` for uniform error handling. + Delegates to the component's underlying `_parse` method and wraps any + exception in a `ComponentParseError` for uniform error handling. Args: computed (ModelOutputThunk): The model output thunk whose value should be parsed. Returns: - S: The parsed result produced by ``_parse``, typed according to the component's type parameter. + S: The parsed result produced by `_parse`, typed according to the component's type parameter. Raises: - ComponentParseError: If the underlying ``_parse`` call raises any exception. + ComponentParseError: If the underlying `_parse` call raises any exception. """ try: return self._parse(computed) @@ -251,7 +251,7 @@ class GenerateType(enum.Enum): Attributes: NONE (None): No generation function has been set; the thunk is either already computed or uninitialized. - ASYNC (int): The generation function is async-compatible; ``avalue``/``astream`` may be used. + ASYNC (int): The generation function is async-compatible; `avalue`/`astream` may be used. SYNC (int): The generation function is synchronous only; async extraction methods are unavailable. """ @@ -307,7 +307,7 @@ class ModelOutputThunk(CBlock, Generic[S]): """A `ModelOutputThunk` is a special type of `CBlock` that we know came from a model's output. It is possible to instantiate one without the output being computed yet. Args: - value (str | None): The raw model output string, or ``None`` if not yet computed. + value (str | None): The raw model output string, or `None` if not yet computed. meta (dict[str, Any] | None): Optional metadata from the inference engine (e.g., completion object). parsed_repr (S | None): An already-parsed representation to attach; set when re-wrapping existing output. tool_calls (dict[str, ModelToolCall] | None): Tool calls returned by the model alongside the text output. @@ -386,26 +386,26 @@ async def cancel_generation(self, error: Exception | None = None) -> None: """Cancel an in-progress streaming generation, drain the queue, and close any open telemetry span. Safe to call at any point during streaming. After this method returns, - ``is_computed()`` is ``True`` and ``value`` contains whatever text was + `is_computed()` is `True` and `value` contains whatever text was accumulated before cancellation. Calling on an already-computed MOT is a no-op. Draining the internal queue after cancellation is necessary to release - any ``asyncio.Queue.put()`` call that the generation task was blocked on + any `asyncio.Queue.put()` call that the generation task was blocked on (queue maxsize=20). Args: error: Optional cause attributed to the open telemetry span. When - provided, this exception is recorded via ``set_span_error`` so + provided, this exception is recorded via `set_span_error` so the span reflects the actual reason for cancellation (e.g. the requirement failure or an unhandled exception from a streaming - validator). When ``None``, a generic - ``RuntimeError("Generation cancelled")`` is recorded. + validator). When `None`, a generic + `RuntimeError("Generation cancelled")` is recorded. Raises: asyncio.CancelledError: Re-raised when the *calling* task itself is - being cancelled (``asyncio.current_task().cancelling() > 0``). - This prevents external cancellation (e.g. ``asyncio.wait_for`` + being cancelled (`asyncio.current_task().cancelling() > 0`). + This prevents external cancellation (e.g. `asyncio.wait_for` timeout) from being silently absorbed while awaiting the inner generation task. """ @@ -482,9 +482,9 @@ def _drain() -> None: @property def cancelled(self) -> bool: - """``True`` if :meth:`cancel_generation` ran to completion on this MOT. + """`True` if :meth:`cancel_generation` ran to completion on this MOT. - A normally-completed MOT leaves this ``False``; only an actual + A normally-completed MOT leaves this `False`; only an actual cancellation via :meth:`cancel_generation` flips it. Consumers holding a computed MOT can use this to distinguish a genuine result from one cut short (for example by a streaming requirement failure). @@ -514,7 +514,7 @@ def is_computed(self) -> bool: """Returns true only if this Thunk has already been filled. Returns: - ``True`` if the thunk value has been set, ``False`` otherwise. + `True` if the thunk value has been set, `False` otherwise. """ return self._computed @@ -794,7 +794,7 @@ class ComputedModelOutputThunk(ModelOutputThunk[S]): the thunk's `__class__` to `ComputedModelOutputThunk` without creating a new object. Args: - thunk: A fully-computed ``ModelOutputThunk`` whose class will be reassigned. + thunk: A fully-computed `ModelOutputThunk` whose class will be reassigned. """ def __new__(cls, thunk: ModelOutputThunk[S]) -> ComputedModelOutputThunk[S]: @@ -853,7 +853,7 @@ def is_computed(self) -> Literal[True]: """Returns `True` since thunk is always computed. Returns: - Always ``True``. + Always `True`. """ return True @@ -864,9 +864,9 @@ class ContextTurn: Args: model_input (CBlock | Component | None): The input component or content block for this turn, - or ``None`` for an output-only partial turn. + or `None` for an output-only partial turn. output (ModelOutputThunk | None): The model's output thunk for this turn, - or ``None`` for an input-only partial turn. + or `None` for an input-only partial turn. """ @@ -883,11 +883,11 @@ class Context(abc.ABC): A context is immutable. Every alteration leads to a new context. Attributes: - is_root_node (bool): ``True`` when this context is the root (empty) node of the linked list. + is_root_node (bool): `True` when this context is the root (empty) node of the linked list. previous_node (Context | None): The context node from which this one was created, - or ``None`` for the root node. + or `None` for the root node. node_data (Component | CBlock | None): The data associated with this context node, - or ``None`` for the root node. + or `None` for the root node. is_chat_context (bool): Whether this context operates in chat (multi-turn) mode. """ @@ -915,7 +915,7 @@ def from_previous( data (Component | CBlock): The component or content block to associate with the new node. Returns: - ContextT: A new context instance whose ``previous_node`` is ``previous``. + ContextT: A new context instance whose `previous_node` is `previous`. """ assert isinstance(previous, Context), ( "Cannot create a new context from a non-Context object." @@ -975,7 +975,7 @@ def as_list(self, last_n_components: int | None = None) -> list[Component | CBlo Args: last_n_components (int | None): Maximum number of most-recent components to include. - Pass ``None`` to return the full history. + Pass `None` to return the full history. Returns: list[Component | CBlock]: Components in chronological order (oldest first). @@ -1006,25 +1006,25 @@ def as_list(self, last_n_components: int | None = None) -> list[Component | CBlo def actions_for_available_tools(self) -> list[Component | CBlock] | None: """Provides a list of actions to extract tools from for use during generation. - Returns ``None`` if it is not possible to construct such a list. Can be used to make + Returns `None` if it is not possible to construct such a list. Can be used to make the available tools differ from the tools of all the actions in the context. Can be overridden by subclasses. Returns: list[Component | CBlock] | None: The list of actions whose tools should be made - available during generation, or ``None`` if unavailable. + available during generation, or `None` if unavailable. """ return self.view_for_generation() def last_output(self, check_last_n_components: int = 3) -> ModelOutputThunk | None: - """Returns the most recent ``ModelOutputThunk`` found within the last N context components. + """Returns the most recent `ModelOutputThunk` found within the last N context components. Args: check_last_n_components (int): Number of most-recent components to search through. Defaults to 3. Returns: - ModelOutputThunk | None: The most recent output thunk, or ``None`` if none is found + ModelOutputThunk | None: The most recent output thunk, or `None` if none is found within the searched components. """ for c in self.as_list(last_n_components=check_last_n_components)[::-1]: @@ -1038,7 +1038,7 @@ def last_turn(self) -> ContextTurn | None: This can be partial. If the last event is an input, then the output is None. Returns: - The most recent turn, or ``None`` if the context is empty. + The most recent turn, or `None` if the context is empty. """ history = self.as_list(last_n_components=2) @@ -1060,13 +1060,13 @@ def last_turn(self) -> ContextTurn | None: @abc.abstractmethod def add(self, c: Component | CBlock) -> Context: - """Returns a new context obtained by appending ``c`` to this context. + """Returns a new context obtained by appending `c` to this context. Args: c (Component | CBlock): The component or content block to add to the context. Returns: - Context: A new context node with ``c`` as its data and this context as its previous node. + Context: A new context node with `c` as its data and this context as its previous node. """ # something along ....from_previous(self, c) ... @@ -1075,12 +1075,12 @@ def add(self, c: Component | CBlock) -> Context: def view_for_generation(self) -> list[Component | CBlock] | None: """Provides a linear list of context components to use for generation. - Returns ``None`` if it is not possible to construct such a list (e.g., the context + Returns `None` if it is not possible to construct such a list (e.g., the context is in an inconsistent state). Concrete subclasses define the ordering and filtering logic. Returns: list[Component | CBlock] | None: An ordered list of components suitable for passing - to a backend, or ``None`` if generation is not currently possible. + to a backend, or `None` if generation is not currently possible. """ ... @@ -1131,7 +1131,7 @@ class TemplateRepresentation: obj (Any): The original component object being represented. args (dict): Named arguments extracted from the component for template substitution. tools (dict[str, AbstractMelleaTool] | None): Tools available for this representation, - keyed by the tool's function name. Defaults to ``None``. + keyed by the tool's function name. Defaults to `None`. fields (list[Any] | None): An optional ordered list of field values for positional templates. template (str | None): An optional Jinja2 template string to use when rendering. template_order (list[str] | None): An optional ordering hint for template sections/keys. @@ -1167,7 +1167,7 @@ class GenerateLog: model_options (dict[str, Any] | None): Model configuration options applied to this call. model_output (Any | None): The raw output returned by the backend API. action (Component | CBlock | None): The component or block that triggered the generation. - result (ModelOutputThunk | None): The ``ModelOutputThunk`` produced by this generation call. + result (ModelOutputThunk | None): The `ModelOutputThunk` produced by this generation call. is_final_result (bool | None): Whether this log entry corresponds to the definitive final result. extra (dict[str, Any] | None): Arbitrary extra metadata to attach to the log entry. @@ -1192,7 +1192,7 @@ class ModelToolCall: Args: name (str): The name of the tool the model requested to call. - func (AbstractMelleaTool): The ``AbstractMelleaTool`` instance that will be invoked. + func (AbstractMelleaTool): The `AbstractMelleaTool` instance that will be invoked. args (Mapping[str, Any]): The keyword arguments the model supplied for the tool call. """ @@ -1205,22 +1205,22 @@ def call_func(self) -> Any: """Invokes the tool represented by this object and returns the result. Returns: - Any: The value returned by ``func.run(**args)``; the concrete type depends on the tool. + Any: The value returned by `func.run(**args)`; the concrete type depends on the tool. """ return self.func.run(**self.args) def blockify(s: str | CBlock | Component) -> CBlock | Component: - """Turn a raw string into a ``CBlock``, leaving ``CBlock`` and ``Component`` objects unchanged. + """Turn a raw string into a `CBlock`, leaving `CBlock` and `Component` objects unchanged. Args: - s: A plain string, ``CBlock``, or ``Component`` to normalise. + s: A plain string, `CBlock`, or `Component` to normalise. Returns: - A ``CBlock`` wrapping ``s`` if it was a string; otherwise ``s`` unchanged. + A `CBlock` wrapping `s` if it was a string; otherwise `s` unchanged. Raises: - Exception: If ``s`` is not a ``str``, ``CBlock``, or ``Component``. + Exception: If `s` is not a `str`, `CBlock`, or `Component`. """ # noinspection PyUnreachableCode match s: @@ -1235,14 +1235,14 @@ def blockify(s: str | CBlock | Component) -> CBlock | Component: def get_images_from_component(c: Component) -> None | list[ImageBlock]: - """Return the images attached to a ``Component``, or ``None`` if absent or empty. + """Return the images attached to a `Component`, or `None` if absent or empty. Args: - c: The ``Component`` whose ``images`` attribute is inspected. + c: The `Component` whose `images` attribute is inspected. Returns: - A non-empty list of ``ImageBlock`` objects if the component has an - ``images`` attribute with at least one element; ``None`` otherwise. + A non-empty list of `ImageBlock` objects if the component has an + `images` attribute with at least one element; `None` otherwise. """ if hasattr(c, "images"): imgs = c.images # type: ignore diff --git a/mellea/core/formatter.py b/mellea/core/formatter.py index faf53d5fa..3db715530 100644 --- a/mellea/core/formatter.py +++ b/mellea/core/formatter.py @@ -1,9 +1,9 @@ -"""Abstract ``Formatter`` interface for rendering components to strings. +"""Abstract `Formatter` interface for rendering components to strings. -A ``Formatter`` converts ``Component`` and ``CBlock`` objects into the text strings -fed to language model prompts. The single abstract method ``print`` encapsulates this -rendering contract; concrete subclasses such as ``ChatFormatter`` and -``TemplateFormatter`` extend it with chat-message and Jinja2-template rendering +A `Formatter` converts `Component` and `CBlock` objects into the text strings +fed to language model prompts. The single abstract method `print` encapsulates this +rendering contract; concrete subclasses such as `ChatFormatter` and +`TemplateFormatter` extend it with chat-message and Jinja2-template rendering respectively. """ @@ -17,12 +17,12 @@ class Formatter(abc.ABC): @abc.abstractmethod def print(self, c: Component | CBlock) -> str: - """Renders a ``Component`` or ``CBlock`` into a string suitable for use as model input. + """Renders a `Component` or `CBlock` into a string suitable for use as model input. Args: c (Component | CBlock): The component or content block to render. Returns: - str: The rendered string representation of ``c``. + str: The rendered string representation of `c`. """ ... diff --git a/mellea/core/requirement.py b/mellea/core/requirement.py index 141af22b0..04b99088a 100644 --- a/mellea/core/requirement.py +++ b/mellea/core/requirement.py @@ -1,12 +1,12 @@ -"""``Requirement`` interface for constrained and validated generation. - -A ``Requirement`` pairs a human-readable description with a validation function that -inspects a ``Context`` (and optionally a backend) to determine whether a model output -meets a constraint. ``ValidationResult`` carries the pass/fail verdict along with an -optional reason, score, and the ``ModelOutputThunk`` produced during validation. -``PartialValidationResult`` provides a tri-state variant (``"pass"``, ``"fail"``, -``"unknown"``) for per-chunk streaming validation. -Helper factories such as ``default_output_to_bool`` make it easy to build requirements +"""`Requirement` interface for constrained and validated generation. + +A `Requirement` pairs a human-readable description with a validation function that +inspects a `Context` (and optionally a backend) to determine whether a model output +meets a constraint. `ValidationResult` carries the pass/fail verdict along with an +optional reason, score, and the `ModelOutputThunk` produced during validation. +`PartialValidationResult` provides a tri-state variant (`"pass"`, `"fail"`, +`"unknown"`) for per-chunk streaming validation. +Helper factories such as `default_output_to_bool` make it easy to build requirements without boilerplate. """ @@ -26,7 +26,7 @@ class ValidationResult: result (bool): Boolean indicating whether the requirement passed. reason (str | None): Optional human-readable explanation for the verdict. score (float | None): Optional numeric score returned by the validator. - thunk (ModelOutputThunk | None): The ``ModelOutputThunk`` produced during LLM-as-a-Judge validation, if applicable. + thunk (ModelOutputThunk | None): The `ModelOutputThunk` produced during LLM-as-a-Judge validation, if applicable. context (Context | None): The context associated with the validation backend call, if applicable. """ @@ -71,7 +71,7 @@ def as_bool(self) -> bool: """Return a boolean value based on the validation result. Returns: - bool: ``True`` if the requirement passed, ``False`` otherwise. + bool: `True` if the requirement passed, `False` otherwise. """ return self._result @@ -88,15 +88,15 @@ class PartialValidationResult: """Tri-state result from per-chunk streaming validation. Unlike :class:`ValidationResult`, which stores its verdict as a private - ``_result: bool``, this class exposes ``success`` as a public property. + `_result: bool`, this class exposes `success` as a public property. The asymmetry is intentional: the tri-state value cannot be recovered from - a ``bool``, so a public property is the only way to distinguish ``"fail"`` - from ``"unknown"`` after construction. + a `bool`, so a public property is the only way to distinguish `"fail"` + from `"unknown"` after construction. Args: - success: Validation state — ``"pass"`` (constraint satisfied so far), - ``"fail"`` (constraint violated, stop streaming), or - ``"unknown"`` (insufficient data yet, continue streaming). + success: Validation state — `"pass"` (constraint satisfied so far), + `"fail"` (constraint violated, stop streaming), or + `"unknown"` (insufficient data yet, continue streaming). reason: Optional human-readable explanation. score: Optional numeric confidence score. thunk: Optional ModelOutputThunk from the validation call. @@ -150,15 +150,15 @@ def context(self) -> Context | None: return self._context def as_bool(self) -> bool: - """Return True for ``"pass"``, False for ``"fail"`` or ``"unknown"``. + """Return True for `"pass"`, False for `"fail"` or `"unknown"`. - ``"unknown"`` maps to ``False`` intentionally. In streaming contexts, - check ``pvr.success == "unknown"`` before treating ``False`` as a definitive - failure — ``"unknown"`` means insufficient data so far, not a constraint + `"unknown"` maps to `False` intentionally. In streaming contexts, + check `pvr.success == "unknown"` before treating `False` as a definitive + failure — `"unknown"` means insufficient data so far, not a constraint violation. Returns: - bool: ``True`` if the partial result is ``"pass"``, ``False`` otherwise. + bool: `True` if the partial result is `"pass"`, `False` otherwise. """ return self._success == "pass" @@ -178,10 +178,10 @@ def default_output_to_bool(x: CBlock | str) -> bool: also checks if any of the words in the output are "yes" (case-insensitive). Args: - x: The model output to evaluate, as a ``CBlock`` or plain string. + x: The model output to evaluate, as a `CBlock` or plain string. Returns: - ``True`` if the output indicates a "yes" answer, ``False`` otherwise. + `True` if the output indicates a "yes" answer, `False` otherwise. """ output = str(x) @@ -200,12 +200,12 @@ class Requirement(Component[str]): Args: description (str | None): A natural-language description of the requirement. Sometimes included in - ``Instruction`` prompts; use ``check_only=True`` to suppress this. + `Instruction` prompts; use `check_only=True` to suppress this. validation_fn (Callable[[Context], ValidationResult] | None): If provided, this function is executed - instead of LLM-as-a-Judge. The ``bool()`` of its return value defines pass/fail. + instead of LLM-as-a-Judge. The `bool()` of its return value defines pass/fail. output_to_bool (Callable[[CBlock | str], bool] | None): Translates LLM-as-a-Judge output to a boolean. Defaults to a "yes"-detection heuristic. - check_only (bool): When ``True``, the requirement description is excluded from ``Instruction`` prompts. + check_only (bool): When `True`, the requirement description is excluded from `Instruction` prompts. Attributes: description (str | None): A natural-language description of the requirement. @@ -213,7 +213,7 @@ class Requirement(Component[str]): output into a boolean pass/fail result. validation_fn (Callable[[Context], ValidationResult] | None): Optional custom validation function that bypasses the LLM-as-a-Judge strategy entirely. - check_only (bool): When ``True``, the requirement description is excluded from ``Instruction`` + check_only (bool): When `True`, the requirement description is excluded from `Instruction` prompts to avoid influencing model output. """ @@ -244,12 +244,12 @@ async def validate( ) -> ValidationResult: """Chooses the appropriate validation strategy and applies it to the given context. - Uses ``validation_fn`` if one was provided, otherwise falls back to LLM-as-a-Judge + Uses `validation_fn` if one was provided, otherwise falls back to LLM-as-a-Judge by generating a judgement response with the backend. Args: backend (Backend): The inference backend used when the LLM-as-a-Judge strategy is selected. - ctx (Context): The context to validate, which must contain a ``ModelOutputThunk`` as its last output. + ctx (Context): The context to validate, which must contain a `ModelOutputThunk` as its last output. format (type[BaseModelSubclass] | None): Optional structured output format for the judgement call. model_options (dict | None): Optional model options to pass to the backend during the judgement call. @@ -288,18 +288,18 @@ async def stream_validate( ) -> PartialValidationResult: """Hook for per-chunk streaming validation. - The default implementation returns ``PartialValidationResult("unknown")`` + The default implementation returns `PartialValidationResult("unknown")` — meaning insufficient data to decide yet. Subclasses override this method - to inspect the current chunk and return ``"pass"`` or ``"fail"`` early. + to inspect the current chunk and return `"pass"` or `"fail"` early. - Implementations may accumulate state on ``self`` across calls within a - single attempt. The orchestrator clones the requirement (``copy(req)``) + Implementations may accumulate state on `self` across calls within a + single attempt. The orchestrator clones the requirement (`copy(req)`) before each attempt, so state does not bleed across retries. - Shallow-copy caveat: mutable container fields (e.g. ``self._buffer = []``) - are shared by reference under ``copy()``. Reassign rather than mutate in - place (``self._buffer = self._buffer + [chunk]``, not - ``self._buffer.append(chunk)``), or override ``__copy__`` for proper + Shallow-copy caveat: mutable container fields (e.g. `self._buffer = []`) + are shared by reference under `copy()`. Reassign rather than mutate in + place (`self._buffer = self._buffer + [chunk]`, not + `self._buffer.append(chunk)`), or override `__copy__` for proper isolation. If an override raises, the enclosing :func:`~mellea.stdlib.streaming.stream_with_chunking` call aborts before any backend generation starts and the exception propagates unchanged. @@ -307,29 +307,29 @@ async def stream_validate( calls) should perform them only after any logic that could raise, since the framework cannot roll them back. - Implementations must not call ``mot.astream()`` or otherwise read the + Implementations must not call `mot.astream()` or otherwise read the underlying stream; the orchestrator is the single consumer of the MOT - stream (see ``ModelOutputThunk.astream``). Requirements that need access + stream (see `ModelOutputThunk.astream`). Requirements that need access to the text seen so far should accumulate it themselves from the - ``chunk`` values they receive. + `chunk` values they receive. Args: chunk: A single complete semantic chunk produced by the chunking - strategy (e.g. one sentence for ``SentenceChunker``). This is - the delta since the previous ``stream_validate`` call for this + strategy (e.g. one sentence for `SentenceChunker`). This is + the delta since the previous `stream_validate` call for this attempt, not the accumulated output. Requirements that need - earlier context should retain it on ``self`` across calls. + earlier context should retain it on `self` across calls. backend: The inference backend, available for backend-assisted checks. ctx: The current generation context. During streaming the MOT is - not yet computed, so ``ctx`` does not contain the generated - output; use ``chunk`` (and any state accumulated on ``self``) + not yet computed, so `ctx` does not contain the generated + output; use `chunk` (and any state accumulated on `self`) instead. Returns: - PartialValidationResult: ``"unknown"`` by default. Subclasses may return - ``"pass"`` (constraint satisfied so far) or ``"fail"`` (constraint violated, - streaming should be aborted). ``"pass"`` does not short-circuit the final - ``validate()`` call; the orchestrator decides whether to skip it. + PartialValidationResult: `"unknown"` by default. Subclasses may return + `"pass"` (constraint satisfied so far) or `"fail"` (constraint violated, + streaming should be aborted). `"pass"` does not short-circuit the final + `validate()` call; the orchestrator decides whether to skip it. """ return PartialValidationResult("unknown") @@ -343,14 +343,14 @@ def parts(self) -> list[Component | CBlock]: return [] def format_for_llm(self) -> TemplateRepresentation | str: - """Returns a ``TemplateRepresentation`` for LLM-as-a-Judge evaluation of this requirement. + """Returns a `TemplateRepresentation` for LLM-as-a-Judge evaluation of this requirement. - Populates the template with the requirement's ``description`` and the stored model - ``_output``. Must only be called from within a ``validate`` call for this same requirement, - after ``_output`` has been set. + Populates the template with the requirement's `description` and the stored model + `_output`. Must only be called from within a `validate` call for this same requirement, + after `_output` has been set. Returns: - TemplateRepresentation | str: A ``TemplateRepresentation`` containing the description + TemplateRepresentation | str: A `TemplateRepresentation` containing the description and the model output to be judged. """ assert self._output is not None, ( diff --git a/mellea/core/sampling.py b/mellea/core/sampling.py index 807922119..0362652c4 100644 --- a/mellea/core/sampling.py +++ b/mellea/core/sampling.py @@ -1,8 +1,8 @@ """Abstract interfaces for sampling strategies and their results. -``SamplingStrategy`` defines the contract for all sampling algorithms: an async -``sample`` method that takes an action, context, backend, and requirements, and -returns a ``SamplingResult``. ``SamplingResult`` records the chosen generation +`SamplingStrategy` defines the contract for all sampling algorithms: an async +`sample` method that takes an action, context, backend, and requirements, and +returns a `SamplingResult`. `SamplingResult` records the chosen generation alongside the full history of intermediate samples, their validation outcomes, and associated contexts — enabling detailed post-hoc inspection of the sampling process. @@ -27,7 +27,7 @@ class SamplingResult(CBlock, Generic[S]): """Stores the results from a sampling operation. This includes successful and failed samplings. Args: - result_index (int): Index into ``sample_generations`` identifying the chosen final output. + result_index (int): Index into `sample_generations` identifying the chosen final output. success (bool): Whether the sampling operation produced a passing result. sample_generations (list[ModelOutputThunk[S]] | None): All output thunks generated during sampling. sample_validations (list[list[tuple[Requirement, ValidationResult]]] | None): Per-generation validation @@ -36,16 +36,16 @@ class SamplingResult(CBlock, Generic[S]): sample_contexts (list[Context] | None): The contexts associated with each generation. Attributes: - result_index (int): Index into ``sample_generations`` identifying the chosen final output. + result_index (int): Index into `sample_generations` identifying the chosen final output. success (bool): Whether the sampling operation produced a passing result. sample_generations (list[ModelOutputThunk[S]]): All output thunks generated during - sampling; always a list (``None`` input is normalised to ``[]``). + sampling; always a list (`None` input is normalised to `[]`). sample_validations (list[list[tuple[Requirement, ValidationResult]]]): Per-generation - validation results; always a list (``None`` input is normalised to ``[]``). + validation results; always a list (`None` input is normalised to `[]`). sample_actions (list[Component]): The actions used to produce each generation; - always a list (``None`` input is normalised to ``[]``). + always a list (`None` input is normalised to `[]`). sample_contexts (list[Context]): The contexts associated with each generation; - always a list (``None`` input is normalised to ``[]``). + always a list (`None` input is normalised to `[]`). """ def __init__( diff --git a/mellea/formatters/__init__.py b/mellea/formatters/__init__.py index c7489dc31..bcbdf96bf 100644 --- a/mellea/formatters/__init__.py +++ b/mellea/formatters/__init__.py @@ -1,11 +1,11 @@ """Formatters for converting components into model-ready prompts. -Formatters translate ``Component`` objects into the prompt strings or chat message -lists that inference backends consume. This package exports the abstract ``Formatter`` -interface and two concrete implementations: ``ChatFormatter``, which converts -components into role-labelled chat messages, and ``TemplateFormatter``, which renders +Formatters translate `Component` objects into the prompt strings or chat message +lists that inference backends consume. This package exports the abstract `Formatter` +interface and two concrete implementations: `ChatFormatter`, which converts +components into role-labelled chat messages, and `TemplateFormatter`, which renders them through Jinja2 templates. Pass a formatter when constructing a -``FormatterBackend`` for your chosen model. +`FormatterBackend` for your chosen model. """ # Import from core for ergonomics. diff --git a/mellea/formatters/chat_formatter.py b/mellea/formatters/chat_formatter.py index 6ccfced05..88a291477 100644 --- a/mellea/formatters/chat_formatter.py +++ b/mellea/formatters/chat_formatter.py @@ -1,9 +1,9 @@ -"""``ChatFormatter`` for converting context histories to chat-message lists. +"""`ChatFormatter` for converting context histories to chat-message lists. -``ChatFormatter`` is the standard formatter used by mellea's legacy backends. Its -``to_chat_messages`` method linearises a sequence of ``Component`` and ``CBlock`` -objects into ``Message`` objects with ``user``, ``assistant``, or ``tool`` roles, -handling ``ModelOutputThunk`` responses, image attachments, and parsed structured +`ChatFormatter` is the standard formatter used by mellea's legacy backends. Its +`to_chat_messages` method linearises a sequence of `Component` and `CBlock` +objects into `Message` objects with `user`, `assistant`, or `tool` roles, +handling `ModelOutputThunk` responses, image attachments, and parsed structured outputs. Concrete backends call this formatter when preparing input for a chat completion endpoint. """ @@ -25,9 +25,9 @@ def to_chat_messages(self, cs: list[Component | CBlock]) -> list[Message]: """Convert a linearized chat history into a list of chat messages. Iterates over each element in the context history and converts it to a - ``Message`` with an appropriate role. ``ModelOutputThunk`` instances are - treated as assistant responses, while all other ``Component`` and - ``CBlock`` objects default to the ``user`` role. Image attachments and + `Message` with an appropriate role. `ModelOutputThunk` instances are + treated as assistant responses, while all other `Component` and + `CBlock` objects default to the `user` role. Image attachments and parsed structured outputs are handled transparently. Args: @@ -35,7 +35,7 @@ def to_chat_messages(self, cs: list[Component | CBlock]) -> list[Message]: components and code blocks to convert. Returns: - list[Message]: A list of ``Message`` objects ready for submission to + list[Message]: A list of `Message` objects ready for submission to a chat completion endpoint. """ diff --git a/mellea/formatters/granite/base/io.py b/mellea/formatters/granite/base/io.py index ece5ecce0..912e1f3a3 100644 --- a/mellea/formatters/granite/base/io.py +++ b/mellea/formatters/granite/base/io.py @@ -35,10 +35,10 @@ def transform( Args: chat_completion (ChatCompletion): Structured representation of the inputs to the chat completion request. - add_generation_prompt (bool): If ``True``, the returned prompt string will + add_generation_prompt (bool): If `True`, the returned prompt string will contain a prefix of the next assistant response for use as a prompt to a generation request. Otherwise, the prompt will only contain the messages - and documents in ``chat_completion``. Defaults to ``True``. + and documents in `chat_completion`. Defaults to `True`. Returns: str: String that can be passed to the model's tokenizer to create a prompt @@ -68,8 +68,8 @@ def transform( model_output (str): String output of the generation request, potentially incomplete if it was a streaming request. chat_completion (ChatCompletion | None): The chat completion request that - produced ``model_output``. Parameters of the request can determine how - the output should be decoded. Defaults to ``None``. + produced `model_output`. Parameters of the request can determine how + the output should be decoded. Defaults to `None`. Returns: AssistantMessage: The parsed output so far, as an instance of @@ -103,7 +103,7 @@ def transform( ChatCompletion: Rewritten copy of the original chat completion request. Raises: - TypeError: If ``chat_completion`` is not a :class:`ChatCompletion` object, + TypeError: If `chat_completion` is not a :class:`ChatCompletion` object, a JSON string, or a dictionary. """ if isinstance(chat_completion, str): @@ -153,16 +153,16 @@ def transform( :class:`ChatCompletionResponse` dataclass, a raw dictionary, or another Pydantic model. chat_completion (ChatCompletion | None): The original chat completion - request that produced ``chat_completion_response``. Required by + request that produced `chat_completion_response`. Required by some implementations to decode references back to the original - request. Defaults to ``None``. + request. Defaults to `None`. Returns: ChatCompletionResponse: Post-processed copy of the chat completion response with model-specific transformations applied. Raises: - TypeError: If ``chat_completion_response`` is not a supported type. + TypeError: If `chat_completion_response` is not a supported type. """ # Convert from over-the-wire format if necessary if isinstance(chat_completion_response, dict): @@ -206,9 +206,9 @@ def retrieve(self, query: str, top_k: int = 10) -> list[Document]: Args: query (str): Query string to use for lookup. - top_k (int): Maximum number of results to return. Defaults to ``10``. + top_k (int): Maximum number of results to return. Defaults to `10`. Returns: list[Document]: List of the top-k matching :class:`Document` objects, - each with fields such as ``text``, ``title``, and ``doc_id``. + each with fields such as `text`, `title`, and `doc_id`. """ diff --git a/mellea/formatters/granite/base/optional.py b/mellea/formatters/granite/base/optional.py index f3de656fd..83590e984 100644 --- a/mellea/formatters/granite/base/optional.py +++ b/mellea/formatters/granite/base/optional.py @@ -2,9 +2,9 @@ """Context-manager helpers for gracefully handling optional import dependencies. -Provides ``import_optional``, a context manager that catches ``ImportError`` and -re-raises it with a human-readable install hint (e.g. ``pip install [extra]``), -and ``nltk_check``, a variant tailored to NLTK data-download errors. Used by Granite +Provides `import_optional`, a context manager that catches `ImportError` and +re-raises it with a human-readable install hint (e.g. `pip install [extra]`), +and `nltk_check`, a variant tailored to NLTK data-download errors. Used by Granite formatter modules that have optional third-party dependencies. """ @@ -27,7 +27,7 @@ def import_optional(extra_name: str): Args: extra_name: Package extra to suggest in the install hint - (e.g. ``pip install mellea[extra_name]``). + (e.g. `pip install mellea[extra_name]`). """ try: yield @@ -49,8 +49,8 @@ def nltk_check(feature_name: str): feature_name: Name of the feature that requires NLTK, used in the error message. Raises: - ImportError: If the ``nltk`` package is not installed or required - NLTK data (e.g. ``punkt_tab``) has not been downloaded, + ImportError: If the `nltk` package is not installed or required + NLTK data (e.g. `punkt_tab`) has not been downloaded, re-raised with a descriptive message and installation instructions. """ diff --git a/mellea/formatters/granite/base/types.py b/mellea/formatters/granite/base/types.py index 8c747c012..928792a88 100644 --- a/mellea/formatters/granite/base/types.py +++ b/mellea/formatters/granite/base/types.py @@ -2,10 +2,10 @@ """Common Pydantic types shared across the Granite formatter package. -Defines reusable Pydantic models and mixins, including ``NoDefaultsMixin`` (which +Defines reusable Pydantic models and mixins, including `NoDefaultsMixin` (which suppresses unset default fields from serialized JSON output) and message/request -types for Granite model chat completions (``ChatMessage``, ``ChatCompletion``, -``VLLMExtraBody``, ``ChatCompletionLogProbs``, and related classes). These types are +types for Granite model chat completions (`ChatMessage`, `ChatCompletion`, +`VLLMExtraBody`, `ChatCompletionLogProbs`, and related classes). These types are consumed internally by the Granite intrinsic formatters. """ @@ -37,7 +37,7 @@ def _workaround_for_design_flaw_in_pydantic(self, nxt): See https://github.com/pydantic/pydantic/issues/4554 for the relevant dismissive comment from the devs. This comment suggests overriding :func:`dict()`, but that method was disabled a year later. Now you need to add a custom serializer method - with a ``@model_serializer`` decorator. + with a `@model_serializer` decorator. See the docs at https://docs.pydantic.dev/latest/api/functional_serializers/ @@ -130,7 +130,7 @@ class UserMessage(_ChatMessageBase): """User message for an IBM Granite model chat completion request. Attributes: - role (str): Always ``"user"``, identifying the message sender. + role (str): Always `"user"`, identifying the message sender. """ role: Literal["user"] = "user" @@ -143,7 +143,7 @@ class DocumentMessage(_ChatMessageBase): completion request. Attributes: - role (str): A string matching the pattern ``"document "``, + role (str): A string matching the pattern `"document "`, identifying this message as a document fragment. """ @@ -180,7 +180,7 @@ class AssistantMessage(_ChatMessageBase): completion request. Attributes: - role (str): Always ``"assistant"``, identifying the message sender. + role (str): Always `"assistant"`, identifying the message sender. tool_calls (list[ToolCall] | None): Optional list of tool calls requested by the assistant during this turn. reasoning_content (str | None): Optional chain-of-thought or reasoning @@ -199,7 +199,7 @@ class ToolResultMessage(_ChatMessageBase): request. Attributes: - role (str): Always ``"tool"``, identifying this as a tool-result message. + role (str): Always `"tool"`, identifying this as a tool-result message. tool_call_id (str): The identifier of the tool call this message responds to. """ @@ -211,7 +211,7 @@ class SystemMessage(_ChatMessageBase): """System message for an IBM Granite model chat completion request. Attributes: - role (str): Always ``"system"``, identifying this as a system-level instruction. + role (str): Always `"system"`, identifying this as a system-level instruction. """ role: Literal["system"] = "system" @@ -221,7 +221,7 @@ class DeveloperMessage(_ChatMessageBase): """Developer system message for a chat completion request. Attributes: - role (str): Always ``"developer"``, identifying this as a developer-role message. + role (str): Always `"developer"`, identifying this as a developer-role message. """ role: Literal["developer"] = "developer" @@ -240,7 +240,7 @@ class DeveloperMessage(_ChatMessageBase): class ToolDefinition(pydantic.BaseModel, NoDefaultsMixin): - """An entry in the ``tools`` list in an IBM Granite model chat completion request. + """An entry in the `tools` list in an IBM Granite model chat completion request. Attributes: name (str): The name used to identify and invoke the tool. @@ -281,7 +281,7 @@ class Document(pydantic.BaseModel, NoDefaultsMixin): class ChatTemplateKwargs(pydantic.BaseModel): """Keyword arguments for chat template. - Values that can appear in the ``chat_template_kwargs`` portion of a valid chat + Values that can appear in the `chat_template_kwargs` portion of a valid chat completion request for a Granite model. Attributes: @@ -308,8 +308,8 @@ class VLLMExtraBody(pydantic.BaseModel, NoDefaultsMixin): Attributes: documents (list[Document] | None): RAG documents made accessible to the model during generation, if the template supports RAG. - add_generation_prompt (bool): When ``True``, the generation prompt is - appended to the rendered chat template. Defaults to ``True``. + add_generation_prompt (bool): When `True`, the generation prompt is + appended to the rendered chat template. Defaults to `True`. chat_template_kwargs (ChatTemplateKwargs | None): Additional keyword arguments forwarded to the chat template renderer. structured_outputs (dict | None): Optional JSON schema that constrains @@ -396,7 +396,7 @@ def _documents(self) -> list[Document] | None: """Fetch documents attached to chat completion. Convenience method for internal code to fetch documents attached to the - chat completion without having to dig into ``extra_body``. + chat completion without having to dig into `extra_body`. """ if self.extra_body: return self.extra_body.documents @@ -406,7 +406,7 @@ def _chat_template_kwargs(self) -> ChatTemplateKwargs | None: """Fetch chat template arguments. Convenience method for internal code to fetch chat template arguments - without having to dig into ``extra_body``. + without having to dig into `extra_body`. """ if self.extra_body: return self.extra_body.chat_template_kwargs @@ -431,7 +431,7 @@ class GraniteChatCompletion(ChatCompletion): def _validate_vllm_stuff_in_extra_body(self): """Validate non-standard VLLM fields. - Non-standard VLLM fields should be passed via the ``extra_body`` parameter. + Non-standard VLLM fields should be passed via the `extra_body` parameter. Make sure the user didn't stuff them into the root, which is currently set up to allow arbitrary additional fields. """ @@ -452,10 +452,10 @@ def _validate_documents_at_top_level(self): """Validate documents at top level. Documents for a Granite model chat completion request should be passed in the - ``documents`` argument at the top level of the ``extra_body`` portion of the + `documents` argument at the top level of the `extra_body` portion of the request. - Detect cases where the documents are hanging off of ``chat_template_kwargs`` + Detect cases where the documents are hanging off of `chat_template_kwargs` and sanitize appropriately. """ if self is None: @@ -528,7 +528,7 @@ class ChatCompletionLogProb(pydantic.BaseModel, NoDefaultsMixin): Attributes: token (str): The decoded token string. logprob (float): The log-probability of the token. Defaults to - ``-9999.0`` when not returned by the server. + `-9999.0` when not returned by the server. bytes (list[int] | None): The UTF-8 byte values of the token, if provided by the server. """ @@ -566,7 +566,7 @@ class ChatCompletionLogProbs(pydantic.BaseModel, NoDefaultsMixin): Attributes: content (list[ChatCompletionLogProbsContent] | None): Per-token - log-probability entries for each generated token, or ``None`` if + log-probability entries for each generated token, or `None` if logprobs were not requested. """ @@ -588,7 +588,7 @@ class ChatCompletionResponseChoice(pydantic.BaseModel, NoDefaultsMixin): logprobs (ChatCompletionLogProbs | None): Token log-probabilities for this choice, if they were requested. finish_reason (str | None): The reason the model stopped generating. - Defaults to ``"stop"`` per the OpenAI specification. + Defaults to `"stop"` per the OpenAI specification. """ index: int diff --git a/mellea/formatters/granite/base/util.py b/mellea/formatters/granite/base/util.py index 79af3ff76..9b01a440a 100644 --- a/mellea/formatters/granite/base/util.py +++ b/mellea/formatters/granite/base/util.py @@ -30,7 +30,7 @@ def import_optional(extra_name: str): Args: extra_name: Package extra to suggest in the install hint - (e.g. ``pip install mellea[extra_name]``). + (e.g. `pip install mellea[extra_name]`). """ try: yield @@ -55,7 +55,7 @@ def find_substring_in_text(substring: str, text: str) -> list[dict]: text: The string to search within. Returns: - List of dicts with ``begin_idx`` and ``end_idx`` for each match found. + List of dicts with `begin_idx` and `end_idx` for each match found. """ span_matches = [] @@ -82,18 +82,18 @@ def load_transformers_lora(local_or_remote_path: str) -> tuple: pass it a LoRA adapter's config, but that auto-loading is very broken as of 8/2025. Workaround powers activate! - Only works if ``transformers`` and ``peft`` are installed. + Only works if `transformers` and `peft` are installed. Args: local_or_remote_path: Local directory path of the LoRA adapter. Returns: - Tuple of ``(model, tokenizer)`` where ``model`` is the loaded LoRA model and - ``tokenizer`` is the corresponding HuggingFace tokenizer. + Tuple of `(model, tokenizer)` where `model` is the loaded LoRA model and + `tokenizer` is the corresponding HuggingFace tokenizer. Raises: - ImportError: If ``peft`` or ``transformers`` packages are not installed. - NotImplementedError: If ``local_or_remote_path`` does not exist locally + ImportError: If `peft` or `transformers` packages are not installed. + NotImplementedError: If `local_or_remote_path` does not exist locally (remote loading from the Hugging Face Hub is not yet implemented). """ with import_optional("peft"): @@ -121,7 +121,7 @@ def chat_completion_request_to_transformers_inputs( """Translate an OpenAI-style chat completion request. Translate an OpenAI-style chat completion request into an input for a Transformers - ``generate()`` call. + `generate()` call. Args: request: Request as parsed JSON or equivalent dataclass. @@ -132,17 +132,17 @@ def chat_completion_request_to_transformers_inputs( constrained_decoding_prefix: Optional generation prefix to append to the prompt. Returns: - Tuple of ``(generate_input, other_input)`` where ``generate_input`` contains - kwargs to pass directly to ``generate()`` and ``other_input`` contains - additional parameters for ``generate_with_transformers``. + Tuple of `(generate_input, other_input)` where `generate_input` contains + kwargs to pass directly to `generate()` and `other_input` contains + additional parameters for `generate_with_transformers`. Raises: - ImportError: If ``torch``, ``transformers``, or ``xgrammar`` packages + ImportError: If `torch`, `transformers`, or `xgrammar` packages are not installed (the latter only when constrained decoding is used). - TypeError: If ``tokenizer.apply_chat_template()`` returns an unexpected type. + TypeError: If `tokenizer.apply_chat_template()` returns an unexpected type. ValueError: If padding or end-of-sequence token IDs cannot be determined from the tokenizer, or if a constrained-decoding request is made - without passing a ``tokenizer`` or ``model`` argument. + without passing a `tokenizer` or `model` argument. """ with import_optional("torch"): # Third Party @@ -315,10 +315,10 @@ def generate_with_transformers( tokenizer: HuggingFace tokenizer for the model, required at several stages of generation. model: Initialized HuggingFace model object. - generate_input: Parameters to pass to the ``generate()`` method, usually - produced by ``chat_completion_request_to_transformers_inputs()``. + generate_input: Parameters to pass to the `generate()` method, usually + produced by `chat_completion_request_to_transformers_inputs()`. other_input: Additional kwargs produced by - ``chat_completion_request_to_transformers_inputs()`` for aspects of the + `chat_completion_request_to_transformers_inputs()` for aspects of the original request that Transformers APIs don't handle natively. Returns: diff --git a/mellea/formatters/granite/granite3/granite32/input.py b/mellea/formatters/granite/granite3/granite32/input.py index 939113973..103149bfb 100644 --- a/mellea/formatters/granite/granite3/granite32/input.py +++ b/mellea/formatters/granite/granite3/granite32/input.py @@ -230,7 +230,7 @@ def sanitize( Args: chat_completion: The chat completion request to sanitize. parts (list[str] | str): Which parts of the chat completion to sanitize; - defaults to ``"all"``. + defaults to `"all"`. Returns: The sanitized chat completion with all Granite 3.2 special tokens @@ -246,17 +246,17 @@ def transform( Args: chat_completion (ChatCompletion): The structured chat completion request to convert into a tokenizer-ready prompt string. - add_generation_prompt (bool): When ``True``, appends the assistant role + add_generation_prompt (bool): When `True`, appends the assistant role header to the end of the prompt to trigger generation. Defaults to - ``True``. + `True`. Returns: str: The prompt string formatted for the Granite 3.2 model tokenizer. Raises: ValueError: If conflicting options are specified, such as enabling - ``thinking`` mode together with documents, tools, or a custom - system message; or enabling ``citations`` or ``hallucinations`` + `thinking` mode together with documents, tools, or a custom + system message; or enabling `citations` or `hallucinations` with a custom system message. """ chat_completion = Granite32ChatCompletion.model_validate( diff --git a/mellea/formatters/granite/granite3/granite32/output.py b/mellea/formatters/granite/granite3/granite32/output.py index dfc12795e..76296bd95 100644 --- a/mellea/formatters/granite/granite3/granite32/output.py +++ b/mellea/formatters/granite/granite3/granite32/output.py @@ -24,7 +24,7 @@ * "response": Model response text without the above constituents This dict is further refined into dataclasses before being returned as an extended -``AssistantMessage``. +`AssistantMessage`. """ # Standard @@ -620,9 +620,9 @@ def transform( Args: model_output (str): Raw text output from the Granite 3.2 model. chat_completion (ChatCompletion | None): The original chat completion - request that produced ``model_output``. Used to determine which + request that produced `model_output`. Used to determine which output features (thinking, tools, citations, hallucinations) to - parse. Defaults to ``None``. + parse. Defaults to `None`. Returns: AssistantMessage: A :class:`Granite3AssistantMessage` containing the diff --git a/mellea/formatters/granite/granite3/granite33/input.py b/mellea/formatters/granite/granite3/granite33/input.py index f671cfd83..4c5bcf831 100644 --- a/mellea/formatters/granite/granite3/granite33/input.py +++ b/mellea/formatters/granite/granite3/granite33/input.py @@ -150,7 +150,7 @@ def sanitize( Args: chat_completion: The chat completion request to sanitize. parts (list[str] | str): Which parts of the chat completion to sanitize; - defaults to ``"all"``. + defaults to `"all"`. Returns: The sanitized chat completion with all Granite 3.3 special tokens @@ -168,17 +168,17 @@ def transform( Args: chat_completion (ChatCompletion): The structured chat completion request to convert into a tokenizer-ready prompt string. - add_generation_prompt (bool): When ``True``, appends the assistant role + add_generation_prompt (bool): When `True`, appends the assistant role header to the end of the prompt to trigger generation. Defaults to - ``True``. + `True`. Returns: str: The prompt string formatted for the Granite 3.3 model tokenizer. Raises: ValueError: If conflicting options are specified, such as enabling - ``thinking`` mode together with documents, tools, or a custom - system message; or enabling ``citations`` or ``hallucinations`` + `thinking` mode together with documents, tools, or a custom + system message; or enabling `citations` or `hallucinations` with a custom system message. """ # Downcast to a Granite-specific request type with possible additional fields. diff --git a/mellea/formatters/granite/granite3/granite33/output.py b/mellea/formatters/granite/granite3/granite33/output.py index 927b58bc7..88585b278 100644 --- a/mellea/formatters/granite/granite3/granite33/output.py +++ b/mellea/formatters/granite/granite3/granite33/output.py @@ -10,7 +10,7 @@ * "response": Model response text without the above constituents This dict is further refined into dataclasses before being returned as an extended -``AssistantMessage``. +`AssistantMessage`. """ # Standard @@ -541,9 +541,9 @@ def transform( Args: model_output (str): Raw text output from the Granite 3.3 model. chat_completion (ChatCompletion | None): The original chat completion - request that produced ``model_output``. Used to determine which + request that produced `model_output`. Used to determine which output features (thinking, tools, citations, hallucinations) to - parse. Defaults to ``None``. + parse. Defaults to `None`. Returns: AssistantMessage: A :class:`Granite3AssistantMessage` containing the diff --git a/mellea/formatters/granite/granite3/input.py b/mellea/formatters/granite/granite3/input.py index 558238f05..eb9b4da7a 100644 --- a/mellea/formatters/granite/granite3/input.py +++ b/mellea/formatters/granite/granite3/input.py @@ -81,7 +81,7 @@ def _message_to_prompt_string(message: UserMessage | AssistantMessage) -> str: def _build_controls_record(chat_completion: Granite3ChatCompletion) -> dict | None: """Build a Granite 3 controls record. - Use the output control flags in ``inputs`` to build a version of the + Use the output control flags in `inputs` to build a version of the undocumented arbitrary JSON data regarding output controls that the Jinja template expected to see in the input for each chat completion request. diff --git a/mellea/formatters/granite/granite3/output.py b/mellea/formatters/granite/granite3/output.py index d3c31d96a..c6d2af0d6 100644 --- a/mellea/formatters/granite/granite3/output.py +++ b/mellea/formatters/granite/granite3/output.py @@ -81,7 +81,7 @@ def parse_hallucinations_text(hallucinations_text: str) -> list[dict]: hallucinations_text: Raw text from the model's "# Hallucinations:" section. Returns: - List of dicts, each with ``hallucination_id``, ``risk``, and ``response_text`` keys. + List of dicts, each with `hallucination_id`, `risk`, and `response_text` keys. """ hallucinations = [] @@ -178,14 +178,14 @@ def add_hallucination_response_spans( Args: hallucination_info: Parsed hallucination list as returned by - ``parse_hallucinations_text``. + `parse_hallucinations_text`. response_text_without_citations: Full response text with citation tags removed. remove_citations_from_response_text: Callable that strips citation tags from a substring of the response. Returns: - Deep copy of ``hallucination_info`` with ``response_text``, ``response_begin``, - and ``response_end`` populated for each entry. + Deep copy of `hallucination_info` with `response_text`, `response_begin`, + and `response_end` populated for each entry. """ augmented_hallucination_info = copy.deepcopy(hallucination_info) @@ -243,11 +243,11 @@ def add_citation_context_spans( Args: citation_info: List of citation dicts as produced by the model output parser. - docs: List of source document dicts, each with ``citation_id``, ``doc_id``, - and ``text`` keys. + docs: List of source document dicts, each with `citation_id`, `doc_id`, + and `text` keys. Returns: - Deep copy of ``citation_info`` with ``context_begin`` and ``context_end`` + Deep copy of `citation_info` with `context_begin` and `context_end` populated for each entry. """ augmented_citation_info = copy.deepcopy(citation_info) diff --git a/mellea/formatters/granite/granite3/types.py b/mellea/formatters/granite/granite3/types.py index 4182537c9..a41c6db95 100644 --- a/mellea/formatters/granite/granite3/types.py +++ b/mellea/formatters/granite/granite3/types.py @@ -24,13 +24,13 @@ class Hallucination(pydantic.BaseModel): Attributes: hallucination_id (str): Unique identifier for the hallucination entry. - risk (str): Risk level of the hallucination, e.g. ``"low"`` or ``"high"``. + risk (str): Risk level of the hallucination, e.g. `"low"` or `"high"`. reasoning (str | None): Optional model-provided reasoning for why this sentence was flagged. response_text (str): The portion of the response text that is flagged. - response_begin (int): Start character offset of ``response_text`` within + response_begin (int): Start character offset of `response_text` within the full response string. - response_end (int): End character offset (exclusive) of ``response_text`` + response_end (int): End character offset (exclusive) of `response_text` within the full response string. """ @@ -49,15 +49,15 @@ class Citation(pydantic.BaseModel): citation_id (str): Unique identifier assigned to this citation. doc_id (str): Identifier of the source document being cited. context_text (str): Verbatim text from the source document that is cited. - context_begin (int): Start character offset of ``context_text`` within + context_begin (int): Start character offset of `context_text` within the source document. - context_end (int): End character offset (exclusive) of ``context_text`` + context_end (int): End character offset (exclusive) of `context_text` within the source document. response_text (str): The portion of the response text that makes this citation. - response_begin (int): Start character offset of ``response_text`` within + response_begin (int): Start character offset of `response_text` within the response string. - response_end (int): End character offset (exclusive) of ``response_text`` + response_end (int): End character offset (exclusive) of `response_text` within the response string. """ @@ -79,14 +79,14 @@ class Granite3Controls(pydantic.BaseModel): and originality style. Attributes: - citations (bool | None): When ``True``, instructs the model to annotate + citations (bool | None): When `True`, instructs the model to annotate factual claims with inline citation markers. - hallucinations (bool | None): When ``True``, instructs the model to + hallucinations (bool | None): When `True`, instructs the model to append a list of sentences that may be hallucinated. - length (str | None): Requested response length; must be ``"short"``, - ``"long"``, or ``None`` for no constraint. + length (str | None): Requested response length; must be `"short"`, + `"long"`, or `None` for no constraint. originality (str | None): Requested response originality style; must be - ``"extractive"``, ``"abstractive"``, or ``None``. + `"extractive"`, `"abstractive"`, or `None`. """ citations: bool | None = None @@ -127,8 +127,8 @@ class Granite3Kwargs(ChatTemplateKwargs, NoDefaultsMixin): controls (Granite3Controls | None): Optional output control flags that enable or configure citations, hallucination detection, response length, and originality style. - thinking (bool): When ``True``, enables chain-of-thought reasoning mode. - Defaults to ``False``. + thinking (bool): When `True`, enables chain-of-thought reasoning mode. + Defaults to `False`. """ controls: Granite3Controls | None = None @@ -158,8 +158,8 @@ def thinking(self) -> bool: """Return whether chain-of-thought thinking mode is enabled. Returns: - bool: ``True`` if the ``thinking`` flag is set in the chat template - kwargs; ``False`` otherwise. + bool: `True` if the `thinking` flag is set in the chat template + kwargs; `False` otherwise. """ kwargs = self.extra_body.chat_template_kwargs if self.extra_body else None return bool(kwargs and isinstance(kwargs, Granite3Kwargs) and kwargs.thinking) @@ -170,7 +170,7 @@ def _validate_chat_template_kwargs(cls, extra_body: VLLMExtraBody) -> VLLMExtraB """Validate Granite 3 chat template kwargs and convert to dataclass. Validates kwargs that are specific to Granite 3 chat templates and converts - the ``chat_template_kwargs`` field to a Granite 3-specific dataclass. + the `chat_template_kwargs` field to a Granite 3-specific dataclass. Other arguments are currently passed through without checking. """ diff --git a/mellea/formatters/granite/intrinsics/constants.py b/mellea/formatters/granite/intrinsics/constants.py index 6a576a89f..d7a6cccbd 100644 --- a/mellea/formatters/granite/intrinsics/constants.py +++ b/mellea/formatters/granite/intrinsics/constants.py @@ -25,7 +25,7 @@ "generative-computing/core-intrinsics-lib", ] """Repositories (aka "models") on Hugging Face Hub that use the old layout of -``//``. +`//`. """ BASE_MODEL_TO_CANONICAL_NAME = { diff --git a/mellea/formatters/granite/intrinsics/input.py b/mellea/formatters/granite/intrinsics/input.py index cd0a3a5e0..f3909e550 100644 --- a/mellea/formatters/granite/intrinsics/input.py +++ b/mellea/formatters/granite/intrinsics/input.py @@ -24,7 +24,7 @@ def _needs_logprobs(transformations: list | None) -> bool: :param transformations: Contents of the field by the same name in the YAML file :type transformations: list - :return: ``True`` if this intrinsic produces a field for which logprobs need to be + :return: `True` if this intrinsic produces a field for which logprobs need to be enabled for downstream result decoding to succeed. :rtype: bool """ @@ -37,7 +37,7 @@ def sentence_delimiter(tag: str, sentence_num: int) -> str: """Return a tag string that identifies the beginning of the indicated sentence. Args: - tag: Tag string prefix, e.g. ``"i"`` or ``"c"``. + tag: Tag string prefix, e.g. `"i"` or `"c"`. sentence_num: Zero-based index of the sentence. Returns: @@ -53,7 +53,7 @@ def mark_sentence_boundaries( """Modify input strings by inserting sentence boundary markers. Modify one or more input strings by inserting a tag in the form - ``<[prefix][number]>`` + `<[prefix][number]>` at the location of each sentence boundary. Args: @@ -89,19 +89,19 @@ def move_documents_to_message( Args: chat_completion: A chat completion request as dataclass or parsed JSON. - how: How to serialize the documents; supported values are ``"string"``, - ``"json"``, and ``"roles"``. + how: How to serialize the documents; supported values are `"string"`, + `"json"`, and `"roles"`. Returns: - A copy of ``chat_completion`` with any documents under ``extra_body`` + A copy of `chat_completion` with any documents under `extra_body` moved to the first message. Returned type will be the same as the input type. May return original object if no edits are necessary. Raises: - TypeError: If ``chat_completion`` is not a :class:`ChatCompletion` or - ``dict``. - ValueError: If ``how`` is not one of ``"string"``, ``"json"``, or - ``"roles"``. + TypeError: If `chat_completion` is not a :class:`ChatCompletion` or + `dict`. + ValueError: If `how` is not one of `"string"`, `"json"`, or + `"roles"`. """ if isinstance(chat_completion, ChatCompletion): should_return_dataclass = True @@ -174,9 +174,9 @@ class IntrinsicsRewriter(ChatCompletionRewriter): Args: config_file (str | pathlib.Path | None): Path to the YAML configuration file for the - target intrinsic. Mutually exclusive with ``config_dict``. + target intrinsic. Mutually exclusive with `config_dict`. config_dict (dict | None): Inline configuration dictionary. Mutually exclusive with - ``config_file``. + `config_file`. model_name (str | None): Optional model name used to locate model-specific overrides within the configuration. @@ -186,18 +186,18 @@ class IntrinsicsRewriter(ChatCompletionRewriter): parameters (dict): Additional parameters (key-value pairs) that this rewriter adds to all chat completion requests. extra_body_parameters (dict): Extended vLLM-specific parameters that go - under the ``extra_body`` element of each request. These are merged - with any existing ``extra_body`` content in incoming requests. + under the `extra_body` element of each request. These are merged + with any existing `extra_body` content in incoming requests. instruction (str | None): Optional instruction template. When present, a new user message is appended with the formatted instruction. sentence_boundaries (dict[str, str] | None): Optional sentence-boundary - marking specification, mapping location strings (``"last_message"`` - or ``"documents"``) to marker prefixes (e.g. ``"c"`` produces - ````, ````, …). + marking specification, mapping location strings (`"last_message"` + or `"documents"`) to marker prefixes (e.g. `"c"` produces + ``, ``, …). docs_as_message (str | None): Optional specification for moving - documents from ``extra_body/documents`` to a user message at the - start of the messages list. Value must be ``"string"``, ``"json"``, - or ``"roles"``. + documents from `extra_body/documents` to a user message at the + start of the messages list. Value must be `"string"`, `"json"`, + or `"roles"`. """ config: dict @@ -211,8 +211,8 @@ class IntrinsicsRewriter(ChatCompletionRewriter): completion requests.""" extra_body_parameters: dict - """Extended vLLM-specific parameters that go under the ``extra_body`` element of - the parameters field. These parameters need to be merged with any ``extra_body`` + """Extended vLLM-specific parameters that go under the `extra_body` element of + the parameters field. These parameters need to be merged with any `extra_body` content that is present in incoming requests.""" instruction: str | None @@ -228,7 +228,7 @@ class IntrinsicsRewriter(ChatCompletionRewriter): docs_as_message: str | None """ - Optional specification for moving documents from ``extra_body/documents`` to a + Optional specification for moving documents from `extra_body/documents` to a user message at the beginning of the messages list. Value specifies how to serialize the documents into the message: "string" or "json". """ diff --git a/mellea/formatters/granite/intrinsics/json_util.py b/mellea/formatters/granite/intrinsics/json_util.py index 573802680..cec4c7c49 100644 --- a/mellea/formatters/granite/intrinsics/json_util.py +++ b/mellea/formatters/granite/intrinsics/json_util.py @@ -2,7 +2,7 @@ """JSON parsing utilities for Granite intrinsic formatters. -Provides a fast, position-aware JSON literal parser (``JsonLiteralWithPosition``) used +Provides a fast, position-aware JSON literal parser (`JsonLiteralWithPosition`) used to extract and re-score tokens inside structured model outputs. The module also defines compiled regular expressions for JSON structural characters, numbers, booleans, and null values that are used throughout the Granite intrinsic formatting pipeline. @@ -68,7 +68,7 @@ def find_string_offsets(json_data: str) -> list[tuple[int, int, str]]: json_data: String containing valid JSON. Returns: - Begin and end offsets of all strings in ``json_data``, including + Begin and end offsets of all strings in `json_data`, including the double quotes. """ result = [] @@ -89,11 +89,11 @@ def non_string_offsets(json_str, compiled_regex, string_begins, string_ends): Args: json_str: Original string of valid JSON data. compiled_regex: Compiled regex for the target token type. - string_begins: Table of string begin offsets within ``json_str``. - string_ends: Table of string end offsets within ``json_str``. + string_begins: Table of string begin offsets within `json_str`. + string_ends: Table of string end offsets within `json_str`. Returns: - List of ``(begin, end, matched_string)`` tuples. + List of `(begin, end, matched_string)` tuples. """ offsets = [] for match in compiled_regex.finditer(json_str): @@ -117,7 +117,7 @@ def tokenize_json(json_str: str): json_str: String representation of valid JSON data. Returns: - List of tuples of ``(begin, end, value, type)``. + List of tuples of `(begin, end, value, type)`. """ string_offsets = find_string_offsets(json_str) string_begins = [s[0] for s in string_offsets] @@ -150,11 +150,11 @@ def reparse_value(tokens, offset) -> tuple[Any, int]: Assumes valid JSON. Args: - tokens: Token stream as produced by ``tokenize_json()``. + tokens: Token stream as produced by `tokenize_json()`. offset: Token offset at which to start parsing. Returns: - Tuple of ``(parsed_value, next_offset)``. + Tuple of `(parsed_value, next_offset)`. Raises: ValueError: If an unexpected delimiter token or unknown token type is @@ -177,19 +177,19 @@ def reparse_value(tokens, offset) -> tuple[Any, int]: def reparse_object(tokens, offset) -> tuple[dict, int]: - """Parse a JSON object from the token stream, starting after the opening ``{``. + """Parse a JSON object from the token stream, starting after the opening `{`. Subroutine called by :func:`reparse_value` when an opening curly brace is - encountered. Consumes tokens until the matching closing ``}`` is found. + encountered. Consumes tokens until the matching closing `}` is found. Args: - tokens: Token stream as produced by ``tokenize_json()``. - offset (int): Token offset immediately after the opening ``{`` delimiter. + tokens: Token stream as produced by `tokenize_json()`. + offset (int): Token offset immediately after the opening `{` delimiter. Returns: - tuple[dict, int]: A tuple of ``(parsed_dict, next_offset)`` where - ``parsed_dict`` maps string keys to parsed values (possibly - :class:`JsonLiteralWithPosition` instances) and ``next_offset`` + tuple[dict, int]: A tuple of `(parsed_dict, next_offset)` where + `parsed_dict` maps string keys to parsed values (possibly + :class:`JsonLiteralWithPosition` instances) and `next_offset` is the position of the next unconsumed token. Raises: @@ -231,19 +231,19 @@ def reparse_object(tokens, offset) -> tuple[dict, int]: def reparse_list(tokens, offset) -> tuple[list, int]: - """Parse a JSON array from the token stream, starting after the opening ``[``. + """Parse a JSON array from the token stream, starting after the opening `[`. Subroutine called by :func:`reparse_value` when an opening square bracket is - encountered. Consumes tokens until the matching closing ``]`` is found. + encountered. Consumes tokens until the matching closing `]` is found. Args: - tokens: Token stream as produced by ``tokenize_json()``. - offset (int): Token offset immediately after the opening ``[`` delimiter. + tokens: Token stream as produced by `tokenize_json()`. + offset (int): Token offset immediately after the opening `[` delimiter. Returns: - tuple[list, int]: A tuple of ``(parsed_list, next_offset)`` where - ``parsed_list`` contains the parsed elements (possibly - :class:`JsonLiteralWithPosition` instances) and ``next_offset`` + tuple[list, int]: A tuple of `(parsed_list, next_offset)` where + `parsed_list` contains the parsed elements (possibly + :class:`JsonLiteralWithPosition` instances) and `next_offset` is the position of the next unconsumed token. Raises: @@ -276,8 +276,8 @@ def reparse_json_with_offsets(json_str: str) -> Any: json_str: String known to contain valid JSON data. Returns: - Parsed representation of ``json_str``, with literals at the leaf nodes of - the parse tree replaced with ``JsonLiteralWithPosition`` instances containing + Parsed representation of `json_str`, with literals at the leaf nodes of + the parse tree replaced with `JsonLiteralWithPosition` instances containing position information. """ tokens = tokenize_json(json_str) @@ -291,7 +291,7 @@ def scalar_paths(parsed_json) -> list[tuple]: parsed_json: JSON data parsed into native Python objects. Returns: - A list of paths to scalar values within ``parsed_json``, where each + A list of paths to scalar values within `parsed_json`, where each path is expressed as a tuple. The root element of a bare scalar is an empty tuple. """ @@ -315,7 +315,7 @@ def all_paths(parsed_json) -> list[tuple]: parsed_json: JSON data parsed into native Python objects. Returns: - A list of paths to all elements of the parse tree of ``parsed_json``, + A list of paths to all elements of the parse tree of `parsed_json`, where each path is expressed as a tuple. The root element of a bare scalar is an empty tuple. """ @@ -335,13 +335,13 @@ def fetch_path(json_value: Any, path: tuple): Args: json_value: Parsed JSON value. path: A tuple of names/numbers that indicates a path from root to a leaf - or internal node of ``json_value``. + or internal node of `json_value`. Returns: The node at the indicated path. Raises: - TypeError: If ``path`` is not a tuple, if a path element is not a string + TypeError: If `path` is not a tuple, if a path element is not a string or integer, or if an intermediate node is not a dict or list. """ if not isinstance(path, tuple): @@ -375,10 +375,10 @@ def replace_path(json_value: Any, path: tuple, new_value: Any) -> Any: new_value: New value to place at the indicated location. Returns: - The modified input, or ``new_value`` itself if the root was replaced. + The modified input, or `new_value` itself if the root was replaced. Raises: - TypeError: If ``path`` is not a tuple, or if any error propagated from + TypeError: If `path` is not a tuple, or if any error propagated from :func:`fetch_path` during path traversal. """ if not isinstance(path, tuple): @@ -395,7 +395,7 @@ def parse_inline_json(json_response: dict) -> dict: """Replace the JSON strings in message contents with parsed JSON. Args: - json_response: Parsed JSON representation of a ``ChatCompletionResponse`` object. + json_response: Parsed JSON representation of a `ChatCompletionResponse` object. Returns: Deep copy of the input with JSON message content strings replaced by parsed @@ -416,12 +416,12 @@ def make_begin_to_token_table(logprobs: ChatCompletionLogProbs | None): """Create a table mapping token begin positions to token indices. Args: - logprobs: The token log probabilities from the chat completion, or ``None`` + logprobs: The token log probabilities from the chat completion, or `None` if the chat completion request did not ask for logprobs. Returns: A dictionary mapping token begin positions to token indices, - or ``None`` if ``logprobs`` is ``None``. + or `None` if `logprobs` is `None`. """ if logprobs is None: return None diff --git a/mellea/formatters/granite/intrinsics/output.py b/mellea/formatters/granite/intrinsics/output.py index a16f8a31b..41ba337e1 100644 --- a/mellea/formatters/granite/intrinsics/output.py +++ b/mellea/formatters/granite/intrinsics/output.py @@ -45,12 +45,12 @@ class TransformationRule(abc.ABC): config (dict): Configuration of the parent output processor, as parsed YAML. input_path_expr (list[str | int | None]): Path expression matching all instances of the field that this rule transforms. Elements can be - strings for object fields, integers for list indices, or ``None`` + strings for object fields, integers for list indices, or `None` for wildcard matches. Attributes: YAML_NAME (str | None): The name used to identify this rule in YAML - configuration files. Subclasses must set this to a non-``None`` string. + configuration files. Subclasses must set this to a non-`None` string. """ YAML_NAME: str | None = None @@ -92,7 +92,7 @@ def _matching_paths(self, parsed_json: Any) -> list[tuple]: :param parsed_json: Output of running model results through :func:`json.loads()`, plus applying zero or more transformation rules. - :returns: List of paths within ``parsed_json`` that match this rule's input + :returns: List of paths within `parsed_json` that match this rule's input path spec. """ return [p for p in json_util.all_paths(parsed_json) if self._is_input_path(p)] @@ -101,10 +101,10 @@ def rule_name(self) -> str: """Return the YAML name that identifies this transformation rule. Returns: - str: The value of ``YAML_NAME`` for this rule subclass. + str: The value of `YAML_NAME` for this rule subclass. Raises: - ValueError: If ``YAML_NAME`` has not been set by the subclass. + ValueError: If `YAML_NAME` has not been set by the subclass. """ if self.YAML_NAME is None: raise ValueError(f"Attempted to fetch missing rule name for {type(self)}") @@ -144,13 +144,13 @@ def apply( through :func:`json_util.reparse_json_with_offsets()`, preserving position information on literal values. logprobs (ChatCompletionLogProbs | None): Optional logprobs result - associated with the original model output string, or ``None`` + associated with the original model output string, or `None` if no logprobs were present. chat_completion (ChatCompletion | None): The chat completion request that produced this output. Required by some rules. Returns: - Any: Transformed copy of ``parsed_json`` after applying this rule. + Any: Transformed copy of `parsed_json` after applying this rule. """ paths = self._matching_paths(parsed_json) prepare_output = self._prepare( @@ -179,7 +179,7 @@ def _apply_at_path(self, result: Any, path: tuple, prepare_output: dict) -> Any: :param prepare_output: Dictionary of global data that this object's :func:`self._prepare()` method has set aside - :returns: A modified version of ``result``, which may be modified in place or + :returns: A modified version of `result`, which may be modified in place or a fresh copy. """ raise NotImplementedError() @@ -188,7 +188,7 @@ def _apply_at_path(self, result: Any, path: tuple, prepare_output: dict) -> Any: class InPlaceTransformation(TransformationRule): """Base class for TransformationRules that replace values in place in JSON. - Base class for ``TransformationRule``s that replace values in place in the source + Base class for `TransformationRule`s that replace values in place in the source JSON. The values replaced can be a scalar, object, or list. """ @@ -218,7 +218,7 @@ def _transform(self, value: Any, path: tuple, prepare_output: dict) -> Any: class AddFieldsTransformation(TransformationRule): """Base class for TransformationRules that add values to JSON. - Base class for ``TransformationRule``s that add one or more values adjacent to + Base class for `TransformationRule`s that add one or more values adjacent to an existing value in the source JSON. """ @@ -231,7 +231,7 @@ def _apply_at_path(self, result: Any, path: tuple, prepare_output: dict) -> Any: :param prepare_output: Dictionary of global data that this object's :func:`self._prepare()` method has set aside - :returns: A modified version of ``result``, which may be modified in place or + :returns: A modified version of `result`, which may be modified in place or a fresh copy. """ if len(path) == 0: @@ -287,11 +287,11 @@ class TokenToFloat(InPlaceTransformation): instances of the field that this rule transforms. categories_to_values (dict[str | int | bool, float] | None): Mapping from categorical labels to floating-point values. Defaults to - ``None``. + `None`. Attributes: YAML_NAME (str): YAML configuration key for this rule; always - ``"likelihood"``. + `"likelihood"`. """ YAML_NAME = "likelihood" @@ -430,7 +430,7 @@ def _desplit_sentences( :param tag: String such as that appears in every sentence boundary marker, e.g. "i" => "" :param first_sentence_num: Number we expect to see in the first sentence boundary - marker in ``target_text``. + marker in `target_text`. :returns: Self-describing dictionary of lists. """ @@ -486,26 +486,26 @@ class DecodeSentences(AddFieldsTransformation): input_path_expr (list[str | int | None]): Path expression matching all instances of the field that this rule transforms. source (str): Name of the location to look for sentences; must be - ``"last_message"`` or ``"documents"``. - output_names (dict): Mapping from output role name (``"begin"``, - ``"end"``, ``"text"``, ``"document_id"``) to the name of the new + `"last_message"` or `"documents"`. + output_names (dict): Mapping from output role name (`"begin"`, + `"end"`, `"text"`, `"document_id"`) to the name of the new field to add in the result JSON. Attributes: YAML_NAME (str): YAML configuration key for this rule; always - ``"decode_sentences"``. + `"decode_sentences"`. begin_name (str | None): Name of the output field that receives the - sentence begin offset; extracted from ``output_names``, or ``None`` + sentence begin offset; extracted from `output_names`, or `None` if not configured. end_name (str | None): Name of the output field that receives the - sentence end offset; extracted from ``output_names``, or ``None`` + sentence end offset; extracted from `output_names`, or `None` if not configured. text_name (str | None): Name of the output field that receives the - sentence text; extracted from ``output_names``, or ``None`` if not + sentence text; extracted from `output_names`, or `None` if not configured. document_id_name (str | None): Name of the output field that receives - the document ID (only used when ``source="documents"``); extracted - from ``output_names``, or ``None`` if not configured. + the document ID (only used when `source="documents"`); extracted + from `output_names`, or `None` if not configured. """ YAML_NAME = "decode_sentences" @@ -526,9 +526,9 @@ def __init__( :param output_names: Names of new result fields to add Raises: - ValueError: If ``source`` is not one of the allowed values, or if - an unexpected key is found in ``output_names``. - TypeError: If ``output_names`` is not a dict. + ValueError: If `source` is not one of the allowed values, or if + an unexpected key is found in `output_names`. + TypeError: If `output_names` is not a dict. """ super().__init__(config, input_path_expr) @@ -731,7 +731,7 @@ class Explode(InPlaceTransformation): Attributes: YAML_NAME (str): YAML configuration key for this rule; always - ``"explode"``. + `"explode"`. target_field (str): Name of the list-valued field within each record to expand. """ @@ -799,7 +799,7 @@ class DropDuplicates(InPlaceTransformation): Attributes: YAML_NAME (str): YAML configuration key for this rule; always - ``"drop_duplicates"``. + `"drop_duplicates"`. target_fields (list): Names of fields used to determine whether two records are considered duplicates. """ @@ -856,7 +856,7 @@ class Project(InPlaceTransformation): Attributes: YAML_NAME (str): YAML configuration key for this rule; always - ``"project"``. + `"project"`. retained_fields (dict): Mapping from original field name to the (possibly renamed) output field name. Initialized from either a list of field names (identity mapping) or an explicit mapping. @@ -907,7 +907,7 @@ class Nest(InPlaceTransformation): Attributes: YAML_NAME (str): YAML configuration key for this rule; always - ``"nest"``. + `"nest"`. field_name (str): Name of the single field in the output JSON object that wraps each matching value. """ @@ -946,11 +946,11 @@ class MergeSpans(InPlaceTransformation): end_field (str): Name of the field that holds the end offset of spans. text_field (str | None): Optional field containing covered text strings that should be concatenated when spans are merged. Defaults to - ``None``. + `None`. Attributes: YAML_NAME (str): YAML configuration key for this rule; always - ``"merge_spans"``. + `"merge_spans"`. """ YAML_NAME = "merge_spans" @@ -1113,16 +1113,16 @@ def _transform(self, value, path, prepare_output): def _find_final_channel_header(token_strings: list[str]) -> int | None: - """Find the token index of the final ``<|message|>`` token in a token sequence. + """Find the token index of the final `<|message|>` token in a token sequence. - Find the token index of ``<|message|>`` that ends the last - ``<|channel|> final <|message|>`` header in the token sequence. + Find the token index of `<|message|>` that ends the last + `<|channel|> final <|message|>` header in the token sequence. Matches are done on exact token values so that the single special token - ``<|channel|>`` is never confused with regular tokens that happen to - concatenate to the same string (e.g. ``['<|', 'channel', '|>']``). + `<|channel|>` is never confused with regular tokens that happen to + concatenate to the same string (e.g. `['<|', 'channel', '|>']`). - :returns: Index of the ``<|message|>`` token, or ``None``. + :returns: Index of the `<|message|>` token, or `None`. """ last_match = None i = 0 @@ -1161,11 +1161,11 @@ def _logprobs_workaround( This function walks the logprob token sequence, matching individual tokens (not concatenated strings) to locate the final channel header. - This ensures that the single special token ``<|channel|>`` is never + This ensures that the single special token `<|channel|>` is never confused with regular tokens that concatenate to the same string. :param logprobs: Logprobs from a chat completion choice. - :returns: ``(content, trimmed_logprobs)`` or ``None`` if no final channel + :returns: `(content, trimmed_logprobs)` or `None` if no final channel is found. """ if logprobs.content is None: @@ -1214,10 +1214,10 @@ class IntrinsicsResultProcessor(ChatCompletionResultProcessor): Args: config_file (str | pathlib.Path | None): Optional path to a YAML - configuration file. Exactly one of ``config_file`` and - ``config_dict`` must be provided. + configuration file. Exactly one of `config_file` and + `config_dict` must be provided. config_dict (dict | None): Optional pre-parsed YAML configuration dict. - Exactly one of ``config_file`` and ``config_dict`` must be + Exactly one of `config_file` and `config_dict` must be provided. """ diff --git a/mellea/formatters/granite/intrinsics/util.py b/mellea/formatters/granite/intrinsics/util.py index 77494f358..a6c88ec6b 100644 --- a/mellea/formatters/granite/intrinsics/util.py +++ b/mellea/formatters/granite/intrinsics/util.py @@ -32,17 +32,17 @@ def make_config_dict( Also parses JSON fields. Args: - config_file: Path to a YAML configuration file. Exactly one of ``config_file`` - and ``config_dict`` must be provided. - config_dict: Pre-parsed configuration dict (from ``yaml.safe_load()``). Exactly - one of ``config_file`` and ``config_dict`` must be provided. + config_file: Path to a YAML configuration file. Exactly one of `config_file` + and `config_dict` must be provided. + config_dict: Pre-parsed configuration dict (from `yaml.safe_load()`). Exactly + one of `config_file` and `config_dict` must be provided. Returns: - Validated configuration dict with optional fields set to ``None`` and JSON + Validated configuration dict with optional fields set to `None` and JSON string fields parsed to Python objects. Raises: - ValueError: If both or neither of ``config_file`` and ``config_dict`` are + ValueError: If both or neither of `config_file` and `config_dict` are provided, if a required field is missing, if an unexpected top-level field is encountered, or if a JSON field cannot be parsed. """ @@ -115,14 +115,14 @@ def obtain_lora( adapter files on local disk. Args: - intrinsic_name: Short name of the intrinsic model, such as ``"certainty"``. + intrinsic_name: Short name of the intrinsic model, such as `"certainty"`. target_model_name: Name of the base model for the LoRA or aLoRA adapter. repo_id: Hugging Face Hub repository containing a collection of LoRA and/or aLoRA adapters for intrinsics. revision: Git revision of the repository to download from. - alora: If ``True``, load the aLoRA version of the intrinsic; otherwise use LoRA. + alora: If `True`, load the aLoRA version of the intrinsic; otherwise use LoRA. cache_dir: Local directory to use as a cache (Hugging Face Hub format), or - ``None`` to use the default location. + `None` to use the default location. file_glob: Only files matching this glob will be downloaded to the cache. Returns: @@ -181,27 +181,27 @@ def obtain_io_yaml( alora: bool = False, cache_dir: str | None = None, ) -> pathlib.Path: - """Download cached ``io.yaml`` configuration file for an intrinsic. + """Download cached `io.yaml` configuration file for an intrinsic. - Downloads an ``io.yaml`` configuration file for an intrinsic + Downloads an `io.yaml` configuration file for an intrinsic with a model repository that follows the format of the [Granite Intrinsics Library]( https://huggingface.co/ibm-granite/granitelib-rag-r1.0) if one is not already in the local cache. Args: - intrinsic_name: Short name of the intrinsic model, such as ``"certainty"``. + intrinsic_name: Short name of the intrinsic model, such as `"certainty"`. target_model_name: Name of the base model for the LoRA or aLoRA adapter. repo_id: Hugging Face Hub repository containing a collection of LoRA and/or aLoRA adapters for intrinsics. revision: Git revision of the repository to download from. - alora: If ``True``, load the aLoRA version of the intrinsic; otherwise use LoRA. + alora: If `True`, load the aLoRA version of the intrinsic; otherwise use LoRA. cache_dir: Local directory to use as a cache (Hugging Face Hub format), or - ``None`` to use the default location. + `None` to use the default location. Returns: - Full path to the local copy of the ``io.yaml`` file, suitable for passing to - ``IntrinsicsRewriter``. + Full path to the local copy of the `io.yaml` file, suitable for passing to + `IntrinsicsRewriter`. """ lora_dir = obtain_lora( intrinsic_name, diff --git a/mellea/formatters/granite/retrievers/elasticsearch.py b/mellea/formatters/granite/retrievers/elasticsearch.py index 092b52490..a29d6caf1 100644 --- a/mellea/formatters/granite/retrievers/elasticsearch.py +++ b/mellea/formatters/granite/retrievers/elasticsearch.py @@ -12,15 +12,15 @@ class ElasticsearchRetriever: retrieve the top-k matching documents for a given natural language query. Attributes: - hosts (str): Full ``url:port`` connection string to the Elasticsearch - server; stored from the ``host`` constructor argument. + hosts (str): Full `url:port` connection string to the Elasticsearch + server; stored from the `host` constructor argument. Args: corpus_name (str): Name of the Elasticsearch index to query. - host (str): Full ``url:port`` connection string to the Elasticsearch + host (str): Full `url:port` connection string to the Elasticsearch server. **kwargs (Any): Additional keyword arguments forwarded to the - ``Elasticsearch`` client constructor. + `Elasticsearch` client constructor. """ def __init__(self, corpus_name: str, host: str, **kwargs: Any): @@ -67,11 +67,11 @@ def retrieve(self, query: str, top_k: int = 5) -> list[dict]: Args: query (str): Natural language query string to search for. - top_k (int): Maximum number of documents to return. Defaults to ``5``. + top_k (int): Maximum number of documents to return. Defaults to `5`. Returns: - list[dict]: List of matching documents, each with keys ``doc_id``, - ``text``, and ``score``. + list[dict]: List of matching documents, each with keys `doc_id`, + `text`, and `score`. """ body = self.create_es_body(top_k, query) diff --git a/mellea/formatters/granite/retrievers/embeddings.py b/mellea/formatters/granite/retrievers/embeddings.py index e1d4c611a..7db1f7952 100644 --- a/mellea/formatters/granite/retrievers/embeddings.py +++ b/mellea/formatters/granite/retrievers/embeddings.py @@ -94,8 +94,8 @@ def compute_embeddings( """Split documents into windows and compute embeddings for each of the the windows. Args: - corpus: PyArrow Table of documents as returned by ``read_corpus()``. - Should have the columns ``["id", "url", "title", "text"]``. + corpus: PyArrow Table of documents as returned by `read_corpus()`. + Should have the columns `["id", "url", "title", "text"]`. embedding_model_name: Hugging Face model name for the model that computes embeddings. Also used for tokenizing. chunk_size: Maximum size of chunks to split documents into, in embedding @@ -106,7 +106,7 @@ def compute_embeddings( Returns: PyArrow Table of chunks of the corpus, with schema - ``["id", "url", "title", "begin", "end", "text", "embedding"]``. + `["id", "url", "title", "begin", "end", "text", "embedding"]`. """ # Third Party import pyarrow as pa @@ -192,7 +192,7 @@ def write_embeddings( Args: target_dir: Location where the files should be written (in a subdirectory). corpus_name: Corpus name used to generate the output directory name. - embeddings: PyArrow Table produced by ``compute_embeddings()``. + embeddings: PyArrow Table produced by `compute_embeddings()`. chunks_per_partition: Number of document chunks to write to each Parquet partition file. @@ -221,8 +221,8 @@ class InMemoryRetriever: Args: data_file_or_table: Parquet file of document snippets and embeddings, or an equivalent - in-memory PyArrow Table. Should have columns ``id``, ``begin``, ``end``, ``text``, - and ``embedding``. + in-memory PyArrow Table. Should have columns `id`, `begin`, `end`, `text`, + and `embedding`. embedding_model_name (str): Name of the Sentence Transformers model to use for embeddings. Must match the model used to compute embeddings in the data file. """ @@ -263,7 +263,7 @@ def retrieve(self, query: str, top_k: int = 5) -> list[dict]: top_k: Number of top results to return. Returns: - List of dicts with keys ``doc_id``, ``text``, and ``score``. + List of dicts with keys `doc_id`, `text`, and `score`. """ # Third Party import pyarrow as pa diff --git a/mellea/formatters/granite/retrievers/util.py b/mellea/formatters/granite/retrievers/util.py index bcc098412..e5552fba7 100644 --- a/mellea/formatters/granite/retrievers/util.py +++ b/mellea/formatters/granite/retrievers/util.py @@ -25,14 +25,14 @@ def download_mtrag_corpus(target_dir: str, corpus_name: str) -> pathlib.Path: Args: target_dir: Location where the file should be written if not already present. - corpus_name: Should be one of ``"cloud"``, ``"clapnq"``, ``"fiqa"``, - or ``"govt"``. + corpus_name: Should be one of `"cloud"`, `"clapnq"`, `"fiqa"`, + or `"govt"`. Returns: Path to the downloaded (or cached) file. Raises: - ValueError: If ``corpus_name`` is not one of the supported corpus names. + ValueError: If `corpus_name` is not one of the supported corpus names. """ corpus_names = ("cloud", "clapnq", "fiqa", "govt") if corpus_name not in corpus_names: @@ -59,10 +59,10 @@ def read_mtrag_corpus(corpus_file: str | pathlib.Path) -> pa.Table: Returns: Documents from the corpus as a PyArrow table, with schema - ``["id", "url", "title", "text"]``. + `["id", "url", "title", "text"]`. Raises: - TypeError: If the ID column cannot be identified or if no ``text`` column + TypeError: If the ID column cannot be identified or if no `text` column is present in the corpus file. """ if not isinstance(corpus_file, pathlib.Path): @@ -105,13 +105,13 @@ def download_mtrag_embeddings(embedding_name: str, corpus_name: str, target_dir: Args: embedding_name: Name of the SentenceTransformers embedding model used to create the embeddings. - corpus_name: Should be one of ``"cloud"``, ``"clapnq"``, ``"fiqa"``, - or ``"govt"``. - target_dir: Location where Parquet files named ``"part_001.parquet"``, - ``"part_002.parquet"``, etc. will be written. + corpus_name: Should be one of `"cloud"`, `"clapnq"`, `"fiqa"`, + or `"govt"`. + target_dir: Location where Parquet files named `"part_001.parquet"`, + `"part_002.parquet"`, etc. will be written. Raises: - ValueError: If ``corpus_name`` is not one of the supported corpus names, or + ValueError: If `corpus_name` is not one of the supported corpus names, or if no precomputed embeddings are found for the given corpus and embedding model combination. """ diff --git a/mellea/formatters/template_formatter.py b/mellea/formatters/template_formatter.py index e21066cc7..1d0974c60 100644 --- a/mellea/formatters/template_formatter.py +++ b/mellea/formatters/template_formatter.py @@ -1,9 +1,9 @@ -"""``TemplateFormatter``: Jinja2-template-based formatter for legacy backends. +"""`TemplateFormatter`: Jinja2-template-based formatter for legacy backends. -``TemplateFormatter`` extends ``ChatFormatter`` to look up a per-component Jinja2 -template at rendering time, allowing each ``Component`` type to control its own -prompt representation. Template discovery walks a configurable ``template_path`` and -the built-in templates directory; results are cached in a ``SimpleLRUCache`` for +`TemplateFormatter` extends `ChatFormatter` to look up a per-component Jinja2 +template at rendering time, allowing each `Component` type to control its own +prompt representation. Template discovery walks a configurable `template_path` and +the built-in templates directory; results are cached in a `SimpleLRUCache` for performance. Use this formatter when your backend requires hand-crafted prompts rather than a generic chat-message rendering. """ @@ -27,8 +27,8 @@ class TemplateFormatter(ChatFormatter): """Formatter that uses Jinja2 templates to render components into prompt strings. - Template discovery walks a configurable ``template_path`` and the built-in - templates directory. Results are optionally cached in a ``SimpleLRUCache`` + Template discovery walks a configurable `template_path` and the built-in + templates directory. Results are optionally cached in a `SimpleLRUCache` for performance. Use this formatter when your backend requires hand-crafted prompts rather than generic chat-message rendering. @@ -37,10 +37,10 @@ class TemplateFormatter(ChatFormatter): Should match the template directory structure. template_path (str): An alternate location where templates can be found. Will be preferred over all other template directories even if a less exact match is found. - Defaults to ``""``. - use_template_cache (bool): When ``True``, caches template lookup results. Set to ``False`` - if you plan to change ``model_id`` or ``template_path`` after construction. - Defaults to ``True``. + Defaults to `""`. + use_template_cache (bool): When `True`, caches template lookup results. Set to `False` + if you plan to change `model_id` or `template_path` after construction. + Defaults to `True`. Example:: diff --git a/mellea/helpers/__init__.py b/mellea/helpers/__init__.py index 62b22eb6a..da1f06724 100644 --- a/mellea/helpers/__init__.py +++ b/mellea/helpers/__init__.py @@ -1,10 +1,10 @@ """Low-level helpers and utilities supporting mellea backends. This package provides the internal plumbing used by the built-in backend -implementations: async utilities (``send_to_queue``, ``wait_for_all_mots``, -``ClientCache``) for managing concurrent model-output thunks; OpenAI-compatible -message conversion helpers (``message_to_openai_message``, ``messages_to_docs``, -``chat_completion_delta_merge``); and ``_ServerType`` detection for adapting +implementations: async utilities (`send_to_queue`, `wait_for_all_mots`, +`ClientCache`) for managing concurrent model-output thunks; OpenAI-compatible +message conversion helpers (`message_to_openai_message`, `messages_to_docs`, +`chat_completion_delta_merge`); and `_ServerType` detection for adapting structured-output support to the target server. Most user code will not import from this package directly — it is consumed internally by the backend layer. """ diff --git a/mellea/helpers/async_helpers.py b/mellea/helpers/async_helpers.py index 0618d54c1..65d419e19 100644 --- a/mellea/helpers/async_helpers.py +++ b/mellea/helpers/async_helpers.py @@ -1,9 +1,9 @@ """Async helper functions for managing concurrent model output thunks. -Provides ``send_to_queue``, which feeds a backend response coroutine or async iterator -into an ``asyncio.Queue`` (including sentinel and error forwarding); ``wait_for_all_mots``, -which gathers multiple ``ModelOutputThunk`` computations in a single ``asyncio.gather`` -call; and ``get_current_event_loop``, a safe wrapper that returns ``None`` instead of +Provides `send_to_queue`, which feeds a backend response coroutine or async iterator +into an `asyncio.Queue` (including sentinel and error forwarding); `wait_for_all_mots`, +which gathers multiple `ModelOutputThunk` computations in a single `asyncio.gather` +call; and `get_current_event_loop`, a safe wrapper that returns `None` instead of raising when no event loop is running. These utilities are used internally by backends that operate in async contexts. """ @@ -23,7 +23,7 @@ async def send_to_queue( Args: co: A coroutine or async iterator producing the backend response. - aqueue: The async queue to send results to. A sentinel ``None`` is appended on + aqueue: The async queue to send results to. A sentinel `None` is appended on completion; an exception instance is appended on error. """ try: @@ -57,7 +57,7 @@ async def wait_for_all_mots(mots: list[ModelOutputThunk]) -> None: functions, session functions, and top-level mellea functions. Args: - mots: List of ``ModelOutputThunk`` objects to await concurrently. + mots: List of `ModelOutputThunk` objects to await concurrently. """ coroutines: list[Coroutine[Any, Any, str]] = [] for mot in mots: @@ -70,7 +70,7 @@ def get_current_event_loop() -> None | asyncio.AbstractEventLoop: """Get the current event loop without having to catch exceptions. Returns: - The running event loop, or ``None`` if no loop is running. + The running event loop, or `None` if no loop is running. """ loop = None try: @@ -113,7 +113,7 @@ def get(self, key: int) -> Any | None: key: Integer cache key. Returns: - The cached value, or ``None`` if the key is not present. + The cached value, or `None` if the key is not present. """ if key not in self.cache: return None diff --git a/mellea/helpers/openai_compatible_helpers.py b/mellea/helpers/openai_compatible_helpers.py index f597edaaa..94c9173df 100644 --- a/mellea/helpers/openai_compatible_helpers.py +++ b/mellea/helpers/openai_compatible_helpers.py @@ -46,13 +46,13 @@ def extract_model_tool_requests( """Extract tool calls from the dict representation of an OpenAI-like chat response object. Args: - tools: Mapping of tool name to ``AbstractMelleaTool`` for lookup. + tools: Mapping of tool name to `AbstractMelleaTool` for lookup. response: Dict representation of an OpenAI-compatible chat completion message - (must contain a ``"message"`` key). + (must contain a `"message"` key). Returns: - Mapping of tool name to ``ModelToolCall`` for each requested tool call, or - ``None`` if no tool calls were found. + Mapping of tool name to `ModelToolCall` for each requested tool call, or + `None` if no tool calls were found. """ model_tool_calls: dict[str, ModelToolCall] = {} calls = response["message"].get("tool_calls", None) @@ -85,19 +85,19 @@ def extract_model_tool_requests( def chat_completion_delta_merge( chunks: list[dict], force_all_tool_calls_separate: bool = False ) -> dict: - """Merge a list of deltas from ``ChatCompletionChunk``s into a single dict representing the ``ChatCompletion`` choice. + """Merge a list of deltas from `ChatCompletionChunk`s into a single dict representing the `ChatCompletion` choice. Args: chunks: The list of dicts that represent the message deltas. - force_all_tool_calls_separate: If ``True``, tool calls in separate message + force_all_tool_calls_separate: If `True`, tool calls in separate message deltas will not be merged even if their index values are the same. Use when providers do not return the correct index value for tool calls; all tool calls must then be fully populated in a single delta. Returns: - A single merged dict representing the assembled ``ChatCompletion`` choice, - with ``finish_reason``, ``index``, and a ``message`` sub-dict containing - ``content``, ``role``, and ``tool_calls``. + A single merged dict representing the assembled `ChatCompletion` choice, + with `finish_reason`, `index`, and a `message` sub-dict containing + `content`, `role`, and `tool_calls`. """ merged: dict[str, Any] = dict() @@ -171,17 +171,17 @@ def chat_completion_delta_merge( def message_to_openai_message(msg: Message, formatter: Formatter | None = None) -> dict: - """Serialise a Mellea ``Message`` to the format required by OpenAI-compatible API providers. + """Serialise a Mellea `Message` to the format required by OpenAI-compatible API providers. Args: - msg: The ``Message`` object to serialise. + msg: The `Message` object to serialise. formatter: Optional formatter used to render the message content (including - documents) through the template system. When ``None``, uses the raw - ``msg.content`` string without document rendering. + documents) through the template system. When `None`, uses the raw + `msg.content` string without document rendering. Returns: - A dict with ``"role"`` and ``"content"`` fields. When the message carries - images, ``"content"`` is a list of text and image-URL dicts; otherwise it + A dict with `"role"` and `"content"` fields. When the message carries + images, `"content"` is a list of text and image-URL dicts; otherwise it is a plain string. """ # NOTE: `self.formatter.to_chat_messages` explicitly skips `Message` objects. However, we need @@ -218,14 +218,14 @@ def message_to_openai_message(msg: Message, formatter: Formatter | None = None) def messages_to_docs(msgs: list[Message]) -> list[dict[str, str]]: - """Extract all ``Document`` objects from a list of ``Message`` objects. + """Extract all `Document` objects from a list of `Message` objects. Args: - msgs: List of ``Message`` objects whose ``_docs`` attributes are inspected. + msgs: List of `Message` objects whose `_docs` attributes are inspected. Returns: - A list of dicts, each with a ``"text"`` key and optional ``"title"`` and - ``"doc_id"`` keys, suitable for passing to an OpenAI-compatible RAG API. + A list of dicts, each with a `"text"` key and optional `"title"` and + `"doc_id"` keys, suitable for passing to an OpenAI-compatible RAG API. """ docs: list[Document] = [] for message in msgs: @@ -247,12 +247,12 @@ def build_completion_usage(output: ModelOutputThunk) -> CompletionUsage | None: """Build a normalized usage object from a model output, if available. Args: - output: Model output object whose ``generation.usage`` mapping contains + output: Model output object whose `generation.usage` mapping contains token counts. Returns: - A ``CompletionUsage`` object when usage metadata is present on the - output, otherwise ``None``. + A `CompletionUsage` object when usage metadata is present on the + output, otherwise `None`. """ if output.generation.usage is None: return None @@ -273,10 +273,10 @@ def has_tool_calls(output: ModelOutputThunk) -> bool: """Check if a model output has tool calls. Args: - output: Model output thunk that may expose a ``tool_calls`` mapping. + output: Model output thunk that may expose a `tool_calls` mapping. Returns: - ``True`` if the output has non-empty tool calls, ``False`` otherwise. + `True` if the output has non-empty tool calls, `False` otherwise. """ return ( hasattr(output, "tool_calls") @@ -290,11 +290,11 @@ def build_tool_calls(output: ModelOutputThunk) -> list[ToolCallDict] | None: """Build OpenAI-compatible tool calls from a model output, if available. Args: - output: Model output thunk that may expose a ``tool_calls`` mapping. + output: Model output thunk that may expose a `tool_calls` mapping. Returns: - List of ``ToolCallDict`` objects when tool calls are present, - otherwise ``None``. + List of `ToolCallDict` objects when tool calls are present, + otherwise `None`. """ if not has_tool_calls(output): return None diff --git a/mellea/helpers/server_type.py b/mellea/helpers/server_type.py index b3a770935..6f0375d03 100644 --- a/mellea/helpers/server_type.py +++ b/mellea/helpers/server_type.py @@ -1,11 +1,11 @@ """Utilities for detecting and classifying the target inference server. -Defines the ``_ServerType`` enum (``LOCALHOST``, ``OPENAI``, ``REMOTE_VLLM``, -``UNKNOWN``) and ``_server_type``, which classifies a URL by hostname. Also provides -``is_vllm_server_with_structured_output``, which probes a server's ``/version`` -endpoint to determine whether it supports the ``structured_outputs`` parameter +Defines the `_ServerType` enum (`LOCALHOST`, `OPENAI`, `REMOTE_VLLM`, +`UNKNOWN`) and `_server_type`, which classifies a URL by hostname. Also provides +`is_vllm_server_with_structured_output`, which probes a server's `/version` +endpoint to determine whether it supports the `structured_outputs` parameter introduced in vLLM ≥ 0.12.0. Used by the OpenAI-compatible backend to choose between -``guided_json`` and ``structured_outputs`` request formats. +`guided_json` and `structured_outputs` request formats. """ import json diff --git a/mellea/plugins/base.py b/mellea/plugins/base.py index f77bd8743..9e4f46fa5 100644 --- a/mellea/plugins/base.py +++ b/mellea/plugins/base.py @@ -71,13 +71,13 @@ async def redact_input(self, payload, ctx): def __init_subclass__( cls, *, name: str = "", priority: int = 50, **kwargs: Any ) -> None: - """Set plugin metadata on subclasses that provide a ``name``.""" + """Set plugin metadata on subclasses that provide a `name`.""" super().__init_subclass__(**kwargs) if name: cls._mellea_plugin_meta = PluginMeta(name=name, priority=priority) # type: ignore[attr-defined] def __enter__(self) -> Any: - """Register this plugin for the duration of a ``with`` block.""" + """Register this plugin for the duration of a `with` block.""" return _plugin_cm_enter(self) def __exit__( @@ -90,7 +90,7 @@ def __exit__( _plugin_cm_exit(self, exc_type, exc_val, exc_tb) async def __aenter__(self) -> Any: - """Async variant — delegates to ``__enter__``.""" + """Async variant — delegates to `__enter__`.""" return self.__enter__() async def __aexit__( @@ -99,7 +99,7 @@ async def __aexit__( exc_val: BaseException | None, exc_tb: TracebackType | None, ) -> None: - """Async variant — delegates to ``__exit__``.""" + """Async variant — delegates to `__exit__`.""" self.__exit__(exc_type, exc_val, exc_tb) @@ -169,9 +169,9 @@ def __init__( # noqa: D107 class MelleaBasePayload(_PayloadBase): # type: ignore[misc,valid-type] """Frozen base — all payloads are immutable by design. - Plugins must use ``model_copy(update={...})`` to propose modifications - and return the copy via ``PluginResult.modified_payload``. The plugin - manager applies the hook's ``HookPayloadPolicy`` to filter changes to + Plugins must use `model_copy(update={...})` to propose modifications + and return the copy via `PluginResult.modified_payload`. The plugin + manager applies the hook's `HookPayloadPolicy` to filter changes to writable fields only. """ @@ -185,9 +185,9 @@ class MelleaBasePayload(_PayloadBase): # type: ignore[misc,valid-type] class MelleaPlugin(_PluginBase): # type: ignore[misc,valid-type] """Base class for Mellea plugins with lifecycle hooks and typed accessors. - Use this when you need lifecycle hooks (``initialize``/``shutdown``) - or typed context accessors. For simpler plugins, prefer ``@hook`` - on standalone functions or ``@plugin`` on plain classes. + Use this when you need lifecycle hooks (`initialize`/`shutdown`) + or typed context accessors. For simpler plugins, prefer `@hook` + on standalone functions or `@plugin` on plain classes. Instances support the context manager protocol for temporary activation:: @@ -213,7 +213,7 @@ def get_backend(self, context: PluginContext) -> Backend | None: context: The plugin context provided by the hook framework. Returns: - The active Backend, or ``None`` if unavailable. + The active Backend, or `None` if unavailable. """ return context.global_context.state.get("backend") @@ -224,7 +224,7 @@ def get_mellea_context(self, context: PluginContext) -> Context | None: context: The plugin context provided by the hook framework. Returns: - The active Mellea Context, or ``None`` if unavailable. + The active Mellea Context, or `None` if unavailable. """ return context.global_context.state.get("context") @@ -235,7 +235,7 @@ def get_session(self, context: PluginContext) -> MelleaSession | None: context: The plugin context provided by the hook framework. Returns: - The active MelleaSession, or ``None`` if unavailable. + The active MelleaSession, or `None` if unavailable. """ return context.global_context.state.get("session") @@ -245,7 +245,7 @@ def plugin_config(self) -> dict[str, Any]: return self._config.config or {} def __enter__(self) -> MelleaPlugin: - """Register this plugin for the duration of a ``with`` block.""" + """Register this plugin for the duration of a `with` block.""" if getattr(self, "_scope_id", None) is not None: raise RuntimeError( f"MelleaPlugin {self.name!r} is already active as a context manager. " @@ -270,11 +270,11 @@ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: self._scope_id = None # type: ignore[assignment] async def __aenter__(self) -> MelleaPlugin: - """Async variant — delegates to the synchronous ``__enter__``.""" + """Async variant — delegates to the synchronous `__enter__`.""" return self.__enter__() async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async variant — delegates to the synchronous ``__exit__``.""" + """Async variant — delegates to the synchronous `__exit__`.""" self.__exit__(exc_type, exc_val, exc_tb) diff --git a/mellea/plugins/context.py b/mellea/plugins/context.py index 2243083a8..4dfe18b21 100644 --- a/mellea/plugins/context.py +++ b/mellea/plugins/context.py @@ -16,19 +16,19 @@ def build_global_context(*, backend: Backend | None = None, **extra_fields: Any) -> Any: - """Build a ContextForge ``GlobalContext`` from Mellea domain objects. + """Build a ContextForge `GlobalContext` from Mellea domain objects. The global context carries lightweight, cross-cutting ambient metadata - (e.g. ``backend_name``) that is useful to every hook regardless of type. + (e.g. `backend_name`) that is useful to every hook regardless of type. Hook-specific data (context, session, action, etc.) belongs on the typed payload, not here. Args: - backend: Optional backend whose ``model_id`` is added to the context. + backend: Optional backend whose `model_id` is added to the context. **extra_fields: Additional key-value pairs merged into the context state. Returns: - A ``GlobalContext`` instance, or ``None`` if ContextForge is not installed. + A `GlobalContext` instance, or `None` if ContextForge is not installed. """ if not _HAS_PLUGIN_FRAMEWORK: return None diff --git a/mellea/plugins/decorators.py b/mellea/plugins/decorators.py index 43bf61af3..9bf438068 100644 --- a/mellea/plugins/decorators.py +++ b/mellea/plugins/decorators.py @@ -13,7 +13,7 @@ class HookMeta: """Metadata attached by the @hook decorator. Args: - hook_type: The hook point name (e.g., ``"generation_pre_call"``). + hook_type: The hook point name (e.g., `"generation_pre_call"`). mode: Execution mode for the hook handler. priority: Execution priority — lower numbers execute first. """ @@ -32,17 +32,17 @@ def hook( """Register an async function or method as a hook handler. Args: - hook_type: The hook point name (e.g., ``"generation_pre_call"``). - mode: Execution mode — ``PluginMode.SEQUENTIAL`` (default), ``PluginMode.CONCURRENT``, - ``PluginMode.AUDIT``, or ``PluginMode.FIRE_AND_FORGET``. - priority: Lower numbers execute first. For methods on a ``Plugin`` subclass, falls back + hook_type: The hook point name (e.g., `"generation_pre_call"`). + mode: Execution mode — `PluginMode.SEQUENTIAL` (default), `PluginMode.CONCURRENT`, + `PluginMode.AUDIT`, or `PluginMode.FIRE_AND_FORGET`. + priority: Lower numbers execute first. For methods on a `Plugin` subclass, falls back to the class-level priority, then 50. For standalone functions, defaults to 50. Returns: - A decorator that attaches ``HookMeta`` to the decorated function. + A decorator that attaches `HookMeta` to the decorated function. Raises: - TypeError: If the decorated function is not ``async def``. + TypeError: If the decorated function is not `async def`. """ def decorator(fn: Callable) -> Callable: diff --git a/mellea/plugins/hooks/component.py b/mellea/plugins/hooks/component.py index b42615c3a..d23936bf1 100644 --- a/mellea/plugins/hooks/component.py +++ b/mellea/plugins/hooks/component.py @@ -8,16 +8,16 @@ class ComponentPreExecutePayload(MelleaBasePayload): - """Payload for ``component_pre_execute`` — before component execution via ``aact()``. + """Payload for `component_pre_execute` — before component execution via `aact()`. Attributes: component_type: Class name of the component being executed. - action: The ``Component`` or ``CBlock`` about to be executed. + action: The `Component` or `CBlock` about to be executed. context_view: Optional snapshot of the context as a list. - requirements: List of ``Requirement`` instances for validation (writable). + requirements: List of `Requirement` instances for validation (writable). model_options: Dict of model options passed to the backend (writable). - format: Optional ``BaseModel`` subclass for structured output / constrained decoding (writable). - strategy: Optional ``SamplingStrategy`` instance controlling retry logic (writable). + format: Optional `BaseModel` subclass for structured output / constrained decoding (writable). + strategy: Optional `SamplingStrategy` instance controlling retry logic (writable). tool_calls_enabled: Whether tool calling is enabled for this execution (writable). """ @@ -32,19 +32,19 @@ class ComponentPreExecutePayload(MelleaBasePayload): class ComponentPostSuccessPayload(MelleaBasePayload): - """Payload for ``component_post_success`` — after successful component execution. + """Payload for `component_post_success` — after successful component execution. Attributes: component_type: Class name of the executed component. - action: The ``Component`` or ``CBlock`` that was executed. + action: The `Component` or `CBlock` that was executed. - result: The ``ModelOutputThunk`` containing the generation result. - context_before: The ``Context`` before execution. + result: The `ModelOutputThunk` containing the generation result. + context_before: The `Context` before execution. - context_after: The ``Context`` after execution (with action + result appended). + context_after: The `Context` after execution (with action + result appended). - generate_log: The ``GenerateLog`` from the final generation pass. - sampling_results: Optional list of ``ModelOutputThunk`` from all sampling attempts. + generate_log: The `GenerateLog` from the final generation pass. + sampling_results: Optional list of `ModelOutputThunk` from all sampling attempts. latency_ms: Wall-clock time for the full execution in milliseconds. """ @@ -59,16 +59,16 @@ class ComponentPostSuccessPayload(MelleaBasePayload): class ComponentPostErrorPayload(MelleaBasePayload): - """Payload for ``component_post_error`` — after component execution fails. + """Payload for `component_post_error` — after component execution fails. Attributes: component_type: Class name of the component that failed. - action: The ``Component`` or ``CBlock`` that was being executed. + action: The `Component` or `CBlock` that was being executed. - error: The ``Exception`` that was raised. - error_type: Class name of the exception (e.g. ``"ValueError"``). + error: The `Exception` that was raised. + error_type: Class name of the exception (e.g. `"ValueError"`). stack_trace: Formatted traceback string. - context: The ``Context`` at the time of the error. + context: The `Context` at the time of the error. model_options: Dict of model options that were in effect. """ diff --git a/mellea/plugins/hooks/generation.py b/mellea/plugins/hooks/generation.py index 18c0e057f..14d143cde 100644 --- a/mellea/plugins/hooks/generation.py +++ b/mellea/plugins/hooks/generation.py @@ -8,15 +8,15 @@ class GenerationPreCallPayload(MelleaBasePayload): - """Payload for ``generation_pre_call`` — before LLM backend call. + """Payload for `generation_pre_call` — before LLM backend call. Attributes: - action: The ``Component`` or ``CBlock`` about to be sent to the backend. + action: The `Component` or `CBlock` about to be sent to the backend. - context: The ``Context`` being used for this generation call. + context: The `Context` being used for this generation call. model_options: Dict of model options (writable — plugins may adjust temperature, etc.). - format: Optional ``BaseModel`` subclass for constrained decoding (writable). + format: Optional `BaseModel` subclass for constrained decoding (writable). tool_calls: Whether tool calls are enabled for this generation (writable). """ @@ -28,18 +28,18 @@ class GenerationPreCallPayload(MelleaBasePayload): class GenerationPostCallPayload(MelleaBasePayload): - """Payload for ``generation_post_call`` — fires once the model output is fully computed. + """Payload for `generation_post_call` — fires once the model output is fully computed. - For lazy ``ModelOutputThunk`` objects this hook fires inside - ``ModelOutputThunk.astream`` after ``post_process`` completes, so - ``model_output.value`` is guaranteed to be available. For already-computed - thunks (e.g. cached responses) it fires before ``generate_from_context`` + For lazy `ModelOutputThunk` objects this hook fires inside + `ModelOutputThunk.astream` after `post_process` completes, so + `model_output.value` is guaranteed to be available. For already-computed + thunks (e.g. cached responses) it fires before `generate_from_context` returns. Attributes: prompt: The formatted prompt sent to the backend (str or list of message dicts). - model_output: The fully-computed ``ModelOutputThunk``. - latency_ms: Elapsed milliseconds from the ``generate_from_context`` call + model_output: The fully-computed `ModelOutputThunk`. + latency_ms: Elapsed milliseconds from the `generate_from_context` call to when the value was fully materialized. """ @@ -49,16 +49,16 @@ class GenerationPostCallPayload(MelleaBasePayload): class GenerationErrorPayload(MelleaBasePayload): - """Payload for ``generation_error`` — fires when the LLM backend raises an exception. + """Payload for `generation_error` — fires when the LLM backend raises an exception. - This hook fires inside ``ModelOutputThunk.astream`` just before the exception + This hook fires inside `ModelOutputThunk.astream` just before the exception is re-raised, giving plugins a chance to observe (but not suppress) the error. Attributes: exception: The exception raised by the backend. - model_output: The ``ModelOutputThunk`` at the time of the error. ``model`` - and ``provider`` are set when the backend set them early (before the - async task); otherwise they are ``None``. + model_output: The `ModelOutputThunk` at the time of the error. `model` + and `provider` are set when the backend set them early (before the + async task); otherwise they are `None`. """ exception: BaseException diff --git a/mellea/plugins/hooks/sampling.py b/mellea/plugins/hooks/sampling.py index 8488e9745..f31004a6d 100644 --- a/mellea/plugins/hooks/sampling.py +++ b/mellea/plugins/hooks/sampling.py @@ -8,15 +8,15 @@ class SamplingLoopStartPayload(MelleaBasePayload): - """Payload for ``sampling_loop_start`` — when sampling strategy begins. + """Payload for `sampling_loop_start` — when sampling strategy begins. Attributes: - strategy_name: Class name of the sampling strategy (e.g. ``"RejectionSamplingStrategy"``). - action: The ``Component`` being sampled. + strategy_name: Class name of the sampling strategy (e.g. `"RejectionSamplingStrategy"`). + action: The `Component` being sampled. - context: The ``Context`` at the start of sampling. + context: The `Context` at the start of sampling. - requirements: List of ``Requirement`` instances to validate against. + requirements: List of `Requirement` instances to validate against. loop_budget: Maximum number of sampling iterations allowed (writable). """ @@ -28,16 +28,16 @@ class SamplingLoopStartPayload(MelleaBasePayload): class SamplingIterationPayload(MelleaBasePayload): - """Payload for ``sampling_iteration`` — after each sampling attempt. + """Payload for `sampling_iteration` — after each sampling attempt. Attributes: - strategy_name: Class name of the sampling strategy (e.g. ``"RejectionSamplingStrategy"``). + strategy_name: Class name of the sampling strategy (e.g. `"RejectionSamplingStrategy"`). iteration: 1-based iteration number within the sampling loop. - action: The ``Component`` used for this attempt. + action: The `Component` used for this attempt. - result: The ``ModelOutputThunk`` produced by this attempt. - validation_results: List of ``(Requirement, ValidationResult)`` tuples. - all_validations_passed: ``True`` when **every** requirement in ``validation_results`` + result: The `ModelOutputThunk` produced by this attempt. + validation_results: List of `(Requirement, ValidationResult)` tuples. + all_validations_passed: `True` when **every** requirement in `validation_results` passed for this iteration (i.e., the sampling attempt succeeded). valid_count: Number of requirements that passed. total_count: Total number of requirements evaluated. @@ -54,16 +54,16 @@ class SamplingIterationPayload(MelleaBasePayload): class SamplingRepairPayload(MelleaBasePayload): - """Payload for ``sampling_repair`` — when repair is invoked after validation failure. + """Payload for `sampling_repair` — when repair is invoked after validation failure. Attributes: - repair_type: Kind of repair (strategy-dependent, e.g. ``"rejection"``, ``"template"``). - failed_action: The ``Component`` that failed validation. + repair_type: Kind of repair (strategy-dependent, e.g. `"rejection"`, `"template"`). + failed_action: The `Component` that failed validation. - failed_result: The ``ModelOutputThunk`` that failed validation. - failed_validations: List of ``(Requirement, ValidationResult)`` tuples that failed. - repair_action: The repaired ``Component`` to use for the next attempt. - repair_context: The ``Context`` to use for the next attempt. + failed_result: The `ModelOutputThunk` that failed validation. + failed_validations: List of `(Requirement, ValidationResult)` tuples that failed. + repair_action: The repaired `Component` to use for the next attempt. + repair_context: The `Context` to use for the next attempt. repair_iteration: 1-based iteration at which the repair was triggered. """ @@ -77,21 +77,21 @@ class SamplingRepairPayload(MelleaBasePayload): class SamplingLoopEndPayload(MelleaBasePayload): - """Payload for ``sampling_loop_end`` — when sampling completes. + """Payload for `sampling_loop_end` — when sampling completes. Attributes: - strategy_name: Class name of the sampling strategy (e.g. ``"RejectionSamplingStrategy"``). - success: ``True`` if at least one attempt passed all requirements. + strategy_name: Class name of the sampling strategy (e.g. `"RejectionSamplingStrategy"`). + success: `True` if at least one attempt passed all requirements. iterations_used: Total number of iterations the loop executed. - final_result: The selected ``ModelOutputThunk`` (best success or best failure). - final_action: The ``Component`` that produced ``final_result``. + final_result: The selected `ModelOutputThunk` (best success or best failure). + final_action: The `Component` that produced `final_result`. - final_context: The ``Context`` associated with ``final_result``. + final_context: The `Context` associated with `final_result`. - failure_reason: Human-readable reason when ``success`` is ``False``. - all_results: List of ``ModelOutputThunk`` from every iteration. - all_validations: Nested list — ``all_validations[i]`` is the list of - ``(Requirement, ValidationResult)`` tuples for iteration *i*. + failure_reason: Human-readable reason when `success` is `False`. + all_results: List of `ModelOutputThunk` from every iteration. + all_validations: Nested list — `all_validations[i]` is the list of + `(Requirement, ValidationResult)` tuples for iteration *i*. """ strategy_name: str = "" diff --git a/mellea/plugins/hooks/session.py b/mellea/plugins/hooks/session.py index 024692228..6933461ff 100644 --- a/mellea/plugins/hooks/session.py +++ b/mellea/plugins/hooks/session.py @@ -11,13 +11,13 @@ class SessionPreInitPayload(MelleaBasePayload): - """Payload for ``session_pre_init`` — before backend initialization. + """Payload for `session_pre_init` — before backend initialization. Attributes: - backend_name: Name of the backend (e.g. ``"ollama"``, ``"openai"``). + backend_name: Name of the backend (e.g. `"ollama"`, `"openai"`). model_id: Model identifier string (writable). model_options: Optional dict of model options like temperature, max_tokens (writable). - context_type: Class name of the context being used (e.g. ``"SimpleContext"``). + context_type: Class name of the context being used (e.g. `"SimpleContext"`). """ backend_name: str @@ -27,12 +27,12 @@ class SessionPreInitPayload(MelleaBasePayload): class SessionPostInitPayload(MelleaBasePayload): - """Payload for ``session_post_init`` — after session is fully initialized. + """Payload for `session_post_init` — after session is fully initialized. Attributes: session_id: UUID string identifying this session. - model_id: Model identifier used by the backend (e.g. ``"granite4.1:3b"``). - context: The initial ``Context`` instance for this session. + model_id: Model identifier used by the backend (e.g. `"granite4.1:3b"`). + context: The initial `Context` instance for this session. """ session_id: str = "" @@ -41,10 +41,10 @@ class SessionPostInitPayload(MelleaBasePayload): class SessionResetPayload(MelleaBasePayload): - """Payload for ``session_reset`` — when session context is reset. + """Payload for `session_reset` — when session context is reset. Attributes: - previous_context: The ``Context`` that is about to be discarded (observe-only). + previous_context: The `Context` that is about to be discarded (observe-only). """ @@ -52,10 +52,10 @@ class SessionResetPayload(MelleaBasePayload): class SessionCleanupPayload(MelleaBasePayload): - """Payload for ``session_cleanup`` — before session cleanup/teardown. + """Payload for `session_cleanup` — before session cleanup/teardown. Attributes: - context: The ``Context`` at the time of cleanup (observe-only). + context: The `Context` at the time of cleanup (observe-only). interaction_count: Number of items in the context at cleanup time. """ diff --git a/mellea/plugins/hooks/tool.py b/mellea/plugins/hooks/tool.py index 8bba52ad3..32dca43e7 100644 --- a/mellea/plugins/hooks/tool.py +++ b/mellea/plugins/hooks/tool.py @@ -8,13 +8,13 @@ class ToolPreInvokePayload(MelleaBasePayload): - """Payload for ``tool_pre_invoke`` — before tool/function invocation. + """Payload for `tool_pre_invoke` — before tool/function invocation. Attributes: - model_tool_call: The ``ModelToolCall`` about to be executed (writable — + model_tool_call: The `ModelToolCall` about to be executed (writable — plugins may modify arguments or swap the tool entirely). - is_control_flow: ``True`` when this tool is used for framework control - flow (e.g. ``final_answer`` in ReAct) rather than data processing. + is_control_flow: `True` when this tool is used for framework control + flow (e.g. `final_answer` in ReAct) rather than data processing. Plugins should check this field to decide whether to act. """ @@ -23,18 +23,18 @@ class ToolPreInvokePayload(MelleaBasePayload): class ToolPostInvokePayload(MelleaBasePayload): - """Payload for ``tool_post_invoke`` — after tool execution. + """Payload for `tool_post_invoke` — after tool execution. Attributes: - model_tool_call: The ``ModelToolCall`` that was executed. + model_tool_call: The `ModelToolCall` that was executed. tool_output: The return value of the tool function (writable — plugins may transform the output before it is formatted). - tool_message: The ``ToolMessage`` constructed from the output. + tool_message: The `ToolMessage` constructed from the output. execution_time_ms: Wall-clock time of the tool execution in milliseconds. - success: ``True`` if the tool executed without raising an exception. - error: The ``Exception`` raised during execution, or ``None`` on success. - is_control_flow: ``True`` when this tool is used for framework control - flow (e.g. ``final_answer`` in ReAct) rather than data processing. + success: `True` if the tool executed without raising an exception. + error: The `Exception` raised during execution, or `None` on success. + is_control_flow: `True` when this tool is used for framework control + flow (e.g. `final_answer` in ReAct) rather than data processing. Plugins should check this field to decide whether to act. """ diff --git a/mellea/plugins/hooks/validation.py b/mellea/plugins/hooks/validation.py index 4c7f22845..78ef539f7 100644 --- a/mellea/plugins/hooks/validation.py +++ b/mellea/plugins/hooks/validation.py @@ -8,13 +8,13 @@ class ValidationPreCheckPayload(MelleaBasePayload): - """Payload for ``validation_pre_check`` — before requirement validation. + """Payload for `validation_pre_check` — before requirement validation. Attributes: - requirements: List of ``Requirement`` instances to validate (writable). - target: The ``CBlock`` being validated, or ``None`` when validating the full context. + requirements: List of `Requirement` instances to validate (writable). + target: The `CBlock` being validated, or `None` when validating the full context. - context: The ``Context`` used for validation. + context: The `Context` used for validation. model_options: Dict of model options for backend-based validators (writable). """ @@ -26,12 +26,12 @@ class ValidationPreCheckPayload(MelleaBasePayload): class ValidationPostCheckPayload(MelleaBasePayload): - """Payload for ``validation_post_check`` — after validation completes. + """Payload for `validation_post_check` — after validation completes. Attributes: - requirements: List of ``Requirement`` instances that were evaluated. - results: List of ``ValidationResult`` instances (writable). - all_validations_passed: ``True`` when every requirement passed (writable). + requirements: List of `Requirement` instances that were evaluated. + results: List of `ValidationResult` instances (writable). + all_validations_passed: `True` when every requirement passed (writable). passed_count: Number of requirements that passed. failed_count: Number of requirements that failed. """ diff --git a/mellea/plugins/manager.py b/mellea/plugins/manager.py index ce2859d38..2dc3c94e1 100644 --- a/mellea/plugins/manager.py +++ b/mellea/plugins/manager.py @@ -74,7 +74,7 @@ def discard_background_tasks() -> None: def has_plugins(hook_type: HookType | None = None) -> bool: """Fast check: are plugins configured and available for the given hook type. - When ``hook_type`` is provided, also checks whether any plugin has + When `hook_type` is provided, also checks whether any plugin has registered a handler for that specific hook, enabling callers to skip payload construction entirely when no plugin subscribes. @@ -82,7 +82,7 @@ def has_plugins(hook_type: HookType | None = None) -> bool: hook_type: Optional hook type to check for registered handlers. Returns: - ``True`` if plugins are enabled and (when ``hook_type`` is given) + `True` if plugins are enabled and (when `hook_type` is given) at least one plugin subscribes to that hook. """ if not _plugins_enabled or _plugin_manager is None: @@ -99,16 +99,16 @@ def is_internal_tool(tool_name: str) -> bool: tool_name: Name of the tool to check. Returns: - ``True`` if the tool is in the internal tools registry. + `True` if the tool is in the internal tools registry. """ return tool_name in _INTERNAL_TOOL_NAMES def get_plugin_manager() -> Any | None: - """Return the initialized PluginManager, or ``None`` if plugins are not configured. + """Return the initialized PluginManager, or `None` if plugins are not configured. Returns: - The singleton ``PluginManager`` instance, or ``None``. + The singleton `PluginManager` instance, or `None`. """ return _plugin_manager @@ -117,7 +117,7 @@ def ensure_plugin_manager() -> Any: """Lazily initialize the PluginManager if not already created. Returns: - The singleton ``PluginManager`` instance. + The singleton `PluginManager` instance. Raises: ImportError: If the ContextForge plugin framework is not installed. @@ -159,7 +159,7 @@ async def initialize_plugins( timeout: Maximum execution time per plugin in seconds. Returns: - The initialized ``PluginManager`` instance. + The initialized `PluginManager` instance. Raises: ImportError: If the ContextForge plugin framework is not installed. @@ -241,23 +241,23 @@ async def invoke_hook( ) -> tuple[Any | None, _MelleaBasePayload]: """Invoke a hook if plugins are configured. - Returns ``(result, possibly-modified-payload)``. - If plugins are not configured, returns ``(None, original_payload)`` immediately. + Returns `(result, possibly-modified-payload)`. + If plugins are not configured, returns `(None, original_payload)` immediately. Three layers of no-op guards ensure zero overhead when plugins are not configured: - 1. ``_plugins_enabled`` boolean — single pointer dereference - 2. ``has_hooks_for(hook_type)`` — skips when no plugin subscribes + 1. `_plugins_enabled` boolean — single pointer dereference + 2. `has_hooks_for(hook_type)` — skips when no plugin subscribes 3. Returns immediately when either guard fails Args: hook_type: The hook point to invoke. payload: The immutable payload to pass to plugin handlers. backend: Optional backend for building the global context. - **context_fields: Additional fields passed to ``build_global_context``. + **context_fields: Additional fields passed to `build_global_context`. Returns: - A ``(result, payload)`` tuple where *result* is the ``PluginResult`` - (or ``None`` when no plugins ran) and *payload* is the + A `(result, payload)` tuple where *result* is the `PluginResult` + (or `None` when no plugins ran) and *payload* is the possibly-modified payload. Raises: diff --git a/mellea/plugins/pluginset.py b/mellea/plugins/pluginset.py index 07ff70bb1..881b69677 100644 --- a/mellea/plugins/pluginset.py +++ b/mellea/plugins/pluginset.py @@ -10,8 +10,8 @@ class PluginSet: """A named, composable group of hook functions and plugin instances. PluginSets are inert containers — they do not register anything themselves. - Registration happens when they are passed to ``register()`` or - ``start_session(plugins=[...])``. + Registration happens when they are passed to `register()` or + `start_session(plugins=[...])`. PluginSets can be nested: a PluginSet can contain other PluginSets. @@ -26,7 +26,7 @@ class PluginSet: Args: name: Human-readable label for this group. - items: Hook functions, plugin instances, or nested ``PluginSet`` instances. + items: Hook functions, plugin instances, or nested `PluginSet` instances. priority: Optional priority override applied to all items in this set. """ @@ -43,13 +43,13 @@ def __init__( # noqa: D107 self._scope_id: str | None = None def flatten(self) -> list[tuple[Callable | Any, int | None]]: - """Recursively flatten nested PluginSets into ``(item, priority_override)`` pairs. + """Recursively flatten nested PluginSets into `(item, priority_override)` pairs. When this set has a priority, it overrides the priorities of all nested - items — including items inside nested ``PluginSet`` instances. + items — including items inside nested `PluginSet` instances. Returns: - Flattened list of ``(item, priority_override)`` pairs. + Flattened list of `(item, priority_override)` pairs. """ result: list[tuple[Callable | Any, int | None]] = [] for item in self.items: @@ -66,7 +66,7 @@ def flatten(self) -> list[tuple[Callable | Any, int | None]]: return result def __enter__(self) -> PluginSet: - """Register all plugins in this set for the duration of the ``with`` block.""" + """Register all plugins in this set for the duration of the `with` block.""" if self._scope_id is not None: raise RuntimeError( f"PluginSet {self.name!r} is already active as a context manager. " @@ -90,11 +90,11 @@ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: self._scope_id = None async def __aenter__(self) -> PluginSet: - """Async variant — delegates to the synchronous ``__enter__``.""" + """Async variant — delegates to the synchronous `__enter__`.""" return self.__enter__() async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: - """Async variant — delegates to the synchronous ``__exit__``.""" + """Async variant — delegates to the synchronous `__exit__`.""" self.__exit__(exc_type, exc_val, exc_tb) def __repr__(self) -> str: # noqa: D105 diff --git a/mellea/plugins/policies.py b/mellea/plugins/policies.py index 262114e29..99ec5ee68 100644 --- a/mellea/plugins/policies.py +++ b/mellea/plugins/policies.py @@ -19,18 +19,18 @@ def _build_policies() -> dict[str, Any]: Mutability is enforced at **two layers**: - 1. **Execution mode** (cpex) — only ``SEQUENTIAL`` and ``TRANSFORM`` plugins - can modify payloads. ``AUDIT``, ``CONCURRENT``, and ``FIRE_AND_FORGET`` + 1. **Execution mode** (cpex) — only `SEQUENTIAL` and `TRANSFORM` plugins + can modify payloads. `AUDIT`, `CONCURRENT`, and `FIRE_AND_FORGET` plugins have their modifications silently discarded by cpex regardless of what this table says. 2. **Field-level policy** (this table) — for modes that *can* modify, this table restricts *which* fields are writable. cpex applies - ``HookPayloadPolicy`` after each plugin returns, accepting only changes + `HookPayloadPolicy` after each plugin returns, accepting only changes to listed fields and discarding the rest. Hooks absent from this table are observe-only; with - ``DefaultHookPolicy.DENY`` (the Mellea default), any modification attempt + `DefaultHookPolicy.DENY` (the Mellea default), any modification attempt on an unlisted hook is rejected by cpex at runtime. """ if not _HAS_PLUGIN_FRAMEWORK: diff --git a/mellea/plugins/registry.py b/mellea/plugins/registry.py index a43ea17ca..3687706ed 100644 --- a/mellea/plugins/registry.py +++ b/mellea/plugins/registry.py @@ -45,14 +45,14 @@ def _map_mode(mode: PluginMode) -> Any: def modify(payload: Any, **field_updates: Any) -> Any: - """Convenience helper for returning a modifying ``PluginResult``. + """Convenience helper for returning a modifying `PluginResult`. - Creates an immutable copy of ``payload`` with ``field_updates`` applied and - wraps it in a ``PluginResult(continue_processing=True)``. Only fields - listed in the hook's ``HookPayloadPolicy.writable_fields`` will be accepted + Creates an immutable copy of `payload` with `field_updates` applied and + wraps it in a `PluginResult(continue_processing=True)`. Only fields + listed in the hook's `HookPayloadPolicy.writable_fields` will be accepted by the framework; changes to read-only fields are silently discarded. - Mirrors ``block()`` for the modification case:: + Mirrors `block()` for the modification case:: # instead of: modified = payload.model_copy(update={"model_output": new_mot}) @@ -66,7 +66,7 @@ def modify(payload: Any, **field_updates: Any) -> Any: **field_updates: Fields to update on the payload copy. Returns: - A ``PluginResult`` with ``continue_processing=True`` and the modified payload. + A `PluginResult` with `continue_processing=True` and the modified payload. Raises: ImportError: If the ContextForge plugin framework is not installed. @@ -89,16 +89,16 @@ def block( description: str = "", details: dict[str, Any] | None = None, ) -> Any: - """Convenience helper for returning a blocking ``PluginResult``. + """Convenience helper for returning a blocking `PluginResult`. Args: reason: Short reason for the violation. code: Machine-readable violation code. - description: Longer description (defaults to ``reason``). + description: Longer description (defaults to `reason`). details: Additional structured details. Returns: - A ``PluginResult`` with ``continue_processing=False`` and a violation. + A `PluginResult` with `continue_processing=False` and a violation. Raises: ImportError: If the ContextForge plugin framework is not installed. @@ -126,16 +126,16 @@ def register( ) -> None: """Register plugins globally or for a specific session. - When ``session_id`` is ``None``, plugins are global (fire for all invocations). - When ``session_id`` is provided, plugins fire only within that session. + When `session_id` is `None`, plugins are global (fire for all invocations). + When `session_id` is provided, plugins fire only within that session. - Accepts standalone ``@hook`` functions, ``@plugin``-decorated class instances, - ``MelleaPlugin`` instances, ``PluginSet`` instances, or lists thereof. + Accepts standalone `@hook` functions, `@plugin`-decorated class instances, + `MelleaPlugin` instances, `PluginSet` instances, or lists thereof. Args: - items: One or more plugins to register — standalone ``@hook`` functions, - ``@plugin``-decorated class instances, ``MelleaPlugin`` instances, - ``PluginSet`` instances, or a list of any combination. + items: One or more plugins to register — standalone `@hook` functions, + `@plugin`-decorated class instances, `MelleaPlugin` instances, + `PluginSet` instances, or a list of any combination. session_id: Optional session identifier. When provided, the plugins are scoped to that session and automatically deregistered on session cleanup. @@ -168,9 +168,9 @@ def _register_single( ) -> None: """Register a single hook function or plugin instance. - - Standalone functions with ``_mellea_hook_meta``: wrapped in ``_FunctionHookAdapter`` - - ``@plugin``-decorated class instances: methods with ``_mellea_hook_meta`` discovered - - ``MelleaPlugin`` instances: registered directly + - Standalone functions with `_mellea_hook_meta`: wrapped in `_FunctionHookAdapter` + - `@plugin`-decorated class instances: methods with `_mellea_hook_meta` discovered + - `MelleaPlugin` instances: registered directly """ meta: HookMeta | None = getattr(item, "_mellea_hook_meta", None) plugin_meta: PluginMeta | None = getattr(type(item), "_mellea_plugin_meta", None) @@ -257,7 +257,7 @@ def _register_single( if _HAS_PLUGIN_FRAMEWORK: class _FunctionHookAdapter(Plugin): - """Adapts a standalone ``@hook``-decorated function into a ContextForge Plugin.""" + """Adapts a standalone `@hook`-decorated function into a ContextForge Plugin.""" def __init__( self, @@ -306,15 +306,15 @@ async def _invoke(self, payload: Any, context: Any) -> Any: return result class _MethodHookAdapter(Plugin): - """Adapts a single ``@hook``-decorated bound method from a ``Plugin`` class. + """Adapts a single `@hook`-decorated bound method from a `Plugin` class. - Each ``@hook`` method on a ``@plugin``-decorated class gets its own adapter - so that per-method execution modes (``SEQUENTIAL``, ``FIRE_AND_FORGET``, etc.) - are respected. The adapter name is ``"."``. + Each `@hook` method on a `@plugin`-decorated class gets its own adapter + so that per-method execution modes (`SEQUENTIAL`, `FIRE_AND_FORGET`, etc.) + are respected. The adapter name is `"."`. - Note: ``initialize()`` and ``shutdown()`` delegate to the underlying class + Note: `initialize()` and `shutdown()` delegate to the underlying class instance and may be called once per registered hook method. Make them - idempotent when using the ``Plugin`` base class with multiple hook methods. + idempotent when using the `Plugin` base class with multiple hook methods. """ def __init__( @@ -368,7 +368,7 @@ async def _invoke(self, payload: Any, context: Any) -> Any: else: # Provide a stub when the plugin framework is not installed. class _FunctionHookAdapter: # type: ignore[no-redef] - """Stub — install ``"mellea[hooks]"`` for full plugin support.""" + """Stub — install `"mellea[hooks]"` for full plugin support.""" def __init__(self, *args: Any, **kwargs: Any) -> None: raise ImportError( @@ -378,7 +378,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: # Provide a stub when the plugin framework is not installed. class _MethodHookAdapter: # type: ignore[no-redef] - """Stub — install ``"mellea[hooks]"`` for full plugin support.""" + """Stub — install `"mellea[hooks]"` for full plugin support.""" def __init__(self, *args: Any, **kwargs: Any) -> None: raise ImportError( @@ -388,9 +388,9 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class _PluginScope: - """Context manager returned by ``plugin_scope()``. + """Context manager returned by `plugin_scope()`. - Supports both synchronous and asynchronous ``with`` statements. + Supports both synchronous and asynchronous `with` statements. """ def __init__(self, items: list[Callable | Any | PluginSet]) -> None: @@ -475,15 +475,15 @@ def unregister( ) -> None: """Unregister globally-registered plugins. - Accepts the same items as ``register()``: standalone ``@hook``-decorated - functions, ``Plugin`` subclass instances, ``MelleaPlugin`` instances, - ``PluginSet`` instances, or lists thereof. + Accepts the same items as `register()`: standalone `@hook`-decorated + functions, `Plugin` subclass instances, `MelleaPlugin` instances, + `PluginSet` instances, or lists thereof. Silently ignores items that are not currently registered. Args: items: One or more plugins to unregister — same types accepted by - ``register()``. + `register()`. Raises: ImportError: If the ContextForge plugin framework is not installed. @@ -514,11 +514,11 @@ def unregister( def plugin_scope(*items: Callable | Any | PluginSet) -> _PluginScope: """Return a context manager that temporarily registers plugins for a block of code. - Accepts the same items as ``register()``: standalone ``@hook``-decorated - functions, ``@plugin``-decorated class instances, ``MelleaPlugin`` instances, - and ``PluginSet`` instances — or any mix thereof. + Accepts the same items as `register()`: standalone `@hook`-decorated + functions, `@plugin`-decorated class instances, `MelleaPlugin` instances, + and `PluginSet` instances — or any mix thereof. - Supports both synchronous and asynchronous ``with`` statements:: + Supports both synchronous and asynchronous `with` statements:: # Sync functional API with plugin_scope(log_hook, audit_plugin): diff --git a/mellea/stdlib/__init__.py b/mellea/stdlib/__init__.py index c517fc759..24bdc4112 100644 --- a/mellea/stdlib/__init__.py +++ b/mellea/stdlib/__init__.py @@ -1,16 +1,16 @@ """The mellea standard library of components, sessions, and sampling strategies. This package provides the high-level building blocks for writing generative programs -with mellea. It contains ready-to-use ``Component`` types (``Instruction``, -``Message``, ``Document``, ``Intrinsic``, ``SimpleComponent``, and more), context -implementations (``ChatContext``, ``SimpleContext``), sampling strategies (rejection -sampling, budget forcing), session management via ``MelleaSession``, and the -``@mify`` decorator for turning ordinary Python objects into components. Import from -the sub-packages — ``mellea.stdlib.components``, ``mellea.stdlib.sampling``, and -``mellea.stdlib.session`` — for day-to-day use. +with mellea. It contains ready-to-use `Component` types (`Instruction`, +`Message`, `Document`, `Intrinsic`, `SimpleComponent`, and more), context +implementations (`ChatContext`, `SimpleContext`), sampling strategies (rejection +sampling, budget forcing), session management via `MelleaSession`, and the +`@mify` decorator for turning ordinary Python objects into components. Import from +the sub-packages — `mellea.stdlib.components`, `mellea.stdlib.sampling`, and +`mellea.stdlib.session` — for day-to-day use. Streaming chunking strategies (for use with streaming validation) are available at -``mellea.stdlib.chunking`` and re-exported here for convenience. The core streaming +`mellea.stdlib.chunking` and re-exported here for convenience. The core streaming orchestration primitive :func:`~mellea.stdlib.streaming.stream_with_chunking` and its result type :class:`~mellea.stdlib.streaming.StreamChunkingResult` are also re-exported here, alongside the full :class:`~mellea.stdlib.streaming.StreamEvent` diff --git a/mellea/stdlib/chunking.py b/mellea/stdlib/chunking.py index 7f426b8af..9e74603f8 100644 --- a/mellea/stdlib/chunking.py +++ b/mellea/stdlib/chunking.py @@ -14,24 +14,24 @@ class ChunkingStrategy(ABC): that has not yet reached a chunk boundary is withheld — it is not included in the returned list. Each call is stateless and idempotent given the same input. - **Performance:** ``split()`` is called on every streaming delta, re-scanning + **Performance:** `split()` is called on every streaming delta, re-scanning the full accumulated text each time (O(n) in total accumulated length per - call). The orchestrator tracks ``prev_chunk_count`` to extract only the new - chunks. This keeps the chunker stateless and removes the need for ``reset()`` + call). The orchestrator tracks `prev_chunk_count` to extract only the new + chunks. This keeps the chunker stateless and removes the need for `reset()` or deep-copy support, at the cost of re-scanning text already seen. For typical model outputs (a few KB) the cost is negligible; for very long streams, a stateful chunker that only processes the new delta would be more efficient. - End-of-stream contract: ``split()`` always withholds the trailing fragment. + End-of-stream contract: `split()` always withholds the trailing fragment. When the stream terminates, callers are responsible for processing any remainder: take the full accumulated text, identify everything after the last returned chunk boundary, and handle it appropriately (e.g. pass to a final validator or discard). Note: this ABC operates on text streams only. Multi-modal output (audio - segments, image regions) is not supported — the ``accumulated_text: str`` - signatures on ``split`` and ``flush`` preclude it. + segments, image regions) is not supported — the `accumulated_text: str` + signatures on `split` and `flush` preclude it. """ @abstractmethod @@ -43,9 +43,9 @@ def split(self, accumulated_text: str) -> list[str]: previously seen tokens and the latest delta. Implementations that scan this string are O(n) in accumulated length per call. Stateful implementations that only process the new delta are - possible but must never mutate state on ``self`` in place — - use reassignment (``self._buf = self._buf + [x]``) so that - ``copy()``-based cloning in the orchestrator works correctly. + possible but must never mutate state on `self` in place — + use reassignment (`self._buf = self._buf + [x]`) so that + `copy()`-based cloning in the orchestrator works correctly. Returns: A list of complete chunks. If no chunk boundary has been reached yet, @@ -54,7 +54,7 @@ def split(self, accumulated_text: str) -> list[str]: ... def flush(self, accumulated_text: str) -> list[str]: - """Return any trailing fragment that ``split`` withheld. + """Return any trailing fragment that `split` withheld. Called once by the orchestrator after the stream has ended naturally (not on early-exit cancellation). Gives the chunker a chance to @@ -68,7 +68,7 @@ def flush(self, accumulated_text: str) -> list[str]: accumulated_text: The full accumulated text at stream end. Returns: - The trailing fragment as ``[fragment]`` if it should be treated + The trailing fragment as `[fragment]` if it should be treated as a final chunk, or an empty list to discard it. """ _ = accumulated_text @@ -92,7 +92,7 @@ def flush(self, accumulated_text: str) -> list[str]: class SentenceChunker(ChunkingStrategy): """Splits accumulated text on sentence boundaries. - Sentence boundaries are detected by ``.``, ``!``, or ``?``, optionally + Sentence boundaries are detected by `.`, `!`, or `?`, optionally followed by a closing quote (straight or curly) or parenthesis, then whitespace. The final sentence is only returned once it is followed by whitespace or another sentence — a trailing fragment with no following @@ -137,9 +137,9 @@ def flush(self, accumulated_text: str) -> list[str]: """Return the trailing sentence fragment (if any) as a final chunk. Trailing whitespace on the fragment is non-semantic for sentence - boundaries and is dropped via ``rstrip``. Leading whitespace is - already removed by the loop's ``lstrip`` on each advance, so no - ``lstrip`` is needed here. The result is the fragment's content + boundaries and is dropped via `rstrip`. Leading whitespace is + already removed by the loop's `lstrip` on each advance, so no + `lstrip` is needed here. The result is the fragment's content only, consistent with how :meth:`split` returns sentences without trailing whitespace. @@ -234,11 +234,11 @@ class ParagraphChunker(ChunkingStrategy): r"""Splits accumulated text on double-newline paragraph boundaries. Two or more consecutive newline characters are treated as a paragraph - separator. The trailing paragraph fragment (text not yet followed by ``\n\n``) + separator. The trailing paragraph fragment (text not yet followed by `\n\n`) is withheld. - Note: only Unix-style ``\n\n`` separators are recognised. CRLF - (``\r\n\r\n``) paragraph separators are not supported. + Note: only Unix-style `\n\n` separators are recognised. CRLF + (`\r\n\r\n`) paragraph separators are not supported. """ def split(self, accumulated_text: str) -> list[str]: @@ -269,7 +269,7 @@ def flush(self, accumulated_text: str) -> list[str]: Unlike :class:`SentenceChunker.flush`, the fragment is returned byte-for-byte without stripping. Internal whitespace — including - a trailing single ``\n`` — can be semantically meaningful inside + a trailing single `\n` — can be semantically meaningful inside a paragraph (e.g. a list item or a deliberate line break), and a consumer validating paragraph content should see the fragment as it was withheld. @@ -280,7 +280,7 @@ def flush(self, accumulated_text: str) -> list[str]: Returns: A single-element list containing the trailing paragraph fragment byte-for-byte, or an empty list when the input ends with a - paragraph boundary (``\n\n`` or more) or is empty. + paragraph boundary (`\n\n` or more) or is empty. """ if not accumulated_text: return [] diff --git a/mellea/stdlib/components/chat.py b/mellea/stdlib/components/chat.py index 1611d043c..e35a83870 100644 --- a/mellea/stdlib/components/chat.py +++ b/mellea/stdlib/components/chat.py @@ -1,11 +1,11 @@ -"""Chat primitives: the ``Message`` and ``ToolMessage`` components. - -Defines ``Message``, the ``Component`` subtype used to represent a single turn in a -chat history with a ``role`` (``user``, ``assistant``, ``system``, or ``tool``), -text ``content``, and optional ``images`` and ``documents`` attachments. Also provides -``ToolMessage`` (a ``Message`` subclass that carries the tool name and arguments), and -utilities for converting a ``Context`` into a flat list of ``Message`` objects: -``as_chat_history`` (strict typing) and ``as_generic_chat_history`` (flexible with +"""Chat primitives: the `Message` and `ToolMessage` components. + +Defines `Message`, the `Component` subtype used to represent a single turn in a +chat history with a `role` (`user`, `assistant`, `system`, or `tool`), +text `content`, and optional `images` and `documents` attachments. Also provides +`ToolMessage` (a `Message` subclass that carries the tool name and arguments), and +utilities for converting a `Context` into a flat list of `Message` objects: +`as_chat_history` (strict typing) and `as_generic_chat_history` (flexible with configurable formatter). """ @@ -31,8 +31,8 @@ class Message(Component["Message"]): """A single Message in a Chat history. Args: - role (str): The role that this message came from (e.g., ``"user"``, - ``"assistant"``). + role (str): The role that this message came from (e.g., `"user"`, + `"assistant"`). content (str): The content of the message. images (list[ImageBlock] | None): Optional images associated with the message. @@ -40,8 +40,8 @@ class Message(Component["Message"]): the message. Attributes: - Role (type): Type alias for the allowed role literals: ``"system"``, - ``"user"``, ``"assistant"``, or ``"tool"``. + Role (type): Type alias for the allowed role literals: `"system"`, + `"user"`, `"assistant"`, or `"tool"`. """ Role = Literal["system", "user", "assistant", "tool"] @@ -189,17 +189,17 @@ class ToolMessage(Message): """Adds the name field for function name. Args: - role (str): The role of this message; most backends use ``"tool"``. + role (str): The role of this message; most backends use `"tool"`. content (str): The content of the message; should be a stringified - version of ``tool_output``. + version of `tool_output`. tool_output (Any): The output of the tool or function call. name (str): The name of the tool or function that was called. args (Mapping[str, Any]): The arguments passed to the tool. - tool (ModelToolCall): The ``ModelToolCall`` representation. + tool (ModelToolCall): The `ModelToolCall` representation. Attributes: arguments (Mapping[str, Any]): The arguments that were passed to the - tool; stored from the ``args`` constructor parameter. + tool; stored from the `args` constructor parameter. """ def __init__( @@ -227,17 +227,17 @@ def as_chat_history(ctx: Context) -> list[Message]: """Returns a list of Messages corresponding to a Context. Args: - ctx: A linear ``Context`` whose entries are ``Message`` or ``ModelOutputThunk`` - objects with ``Message`` parsed representations. + ctx: A linear `Context` whose entries are `Message` or `ModelOutputThunk` + objects with `Message` parsed representations. Returns: - List of ``Message`` objects in conversation order. + List of `Message` objects in conversation order. Raises: ValueError: If the context history is non-linear and cannot be cast to a flat list. AssertionError: If any entry in the context cannot be converted to a - ``Message``. + `Message`. """ def _to_msg(c: CBlock | Component | ModelOutputThunk) -> Message | None: @@ -279,27 +279,27 @@ def as_generic_chat_history( ) -> list[Message]: """Returns a list of Messages corresponding to a Context, with flexible type handling. - This function is more permissive than ``as_chat_history()``, allowing arbitrary + This function is more permissive than `as_chat_history()`, allowing arbitrary component types. Unknown types are converted to strings using a configurable formatter, making it suitable for general-purpose use where context composition may be heterogeneous. The formatter is applied to: - - ``ModelOutputThunk`` with non-Message ``parsed_repr`` - - ``CBlock`` subclasses (subclasses only; plain ``CBlock`` is stringified) + - `ModelOutputThunk` with non-Message `parsed_repr` + - `CBlock` subclasses (subclasses only; plain `CBlock` is stringified) - Other unknown component types - Existing ``Message`` objects are preserved as-is; their content is not formatted. + Existing `Message` objects are preserved as-is; their content is not formatted. This design preserves Message fidelity while providing an escape hatch for unknown types. Args: - ctx: A linear ``Context`` that may contain ``Message``, ``ModelOutputThunk``, - or other ``Component`` types. + ctx: A linear `Context` that may contain `Message`, `ModelOutputThunk`, + or other `Component` types. formatter: Optional callable that converts unknown types to strings. - Defaults to ``_default_formatter`` which logs a warning and stringifies. + Defaults to `_default_formatter` which logs a warning and stringifies. Returns: - List of ``Message`` objects in conversation order. + List of `Message` objects in conversation order. Raises: ValueError: If the context history is non-linear and cannot be cast to a diff --git a/mellea/stdlib/components/docs/document.py b/mellea/stdlib/components/docs/document.py index b355501f5..6a2f6871f 100644 --- a/mellea/stdlib/components/docs/document.py +++ b/mellea/stdlib/components/docs/document.py @@ -1,8 +1,8 @@ -"""``Document`` component for grounding model inputs with text passages. +"""`Document` component for grounding model inputs with text passages. -``Document`` wraps a text passage with an optional ``title`` and ``doc_id``, and +`Document` wraps a text passage with an optional `title` and `doc_id`, and renders them inline as a formatted citation string for the model. Documents are -typically attached to a ``Message`` via its ``documents`` parameter, enabling +typically attached to a `Message` via its `documents` parameter, enabling retrieval-augmented generation (RAG) workflows. """ @@ -15,7 +15,7 @@ class Document(Component[str]): """A text passage with optional metadata for grounding model inputs. - Documents are typically attached to a ``Message`` via its ``documents`` + Documents are typically attached to a `Message` via its `documents` parameter to enable retrieval-augmented generation (RAG) workflows. Args: @@ -36,13 +36,13 @@ def parts(self) -> list[Component | CBlock]: Returns: list[Component | CBlock]: An empty list by default since the base - ``Document`` class has no constituent parts. Subclasses may override + `Document` class has no constituent parts. Subclasses may override this method to return meaningful parts. """ return [] def format_for_llm(self) -> TemplateRepresentation: - """Formats the `Document` as a ``TemplateRepresentation``. + """Formats the `Document` as a `TemplateRepresentation`. Returns: a TemplateRepresentation with text, title, and doc_id args. """ @@ -68,7 +68,7 @@ def _coerce_to_documents( documents: Strings, Document objects, a mix, or None. auto_doc_id: When True, assign sequential string doc_id values ("0", "1", ...) to Documents created from strings and warn about - existing Document objects that have no ``doc_id`` set. + existing Document objects that have no `doc_id` set. Returns: A list of Document objects, or None if the input was None. diff --git a/mellea/stdlib/components/docs/richdocument.py b/mellea/stdlib/components/docs/richdocument.py index feb1bbd53..ba56f4da0 100644 --- a/mellea/stdlib/components/docs/richdocument.py +++ b/mellea/stdlib/components/docs/richdocument.py @@ -1,10 +1,10 @@ -"""``RichDocument``, ``Table``, and related helpers backed by Docling. +"""`RichDocument`, `Table`, and related helpers backed by Docling. -``RichDocument`` wraps a ``DoclingDocument`` (e.g. produced by converting a PDF or -Markdown file) and renders it as Markdown for a language model. ``Table`` represents a -single table within a Docling document and provides ``transpose``, ``to_markdown``, and -query/transform helpers. Use ``RichDocument.from_document_file`` to convert a PDF or -other supported format, and ``get_tables()`` to extract structured table data for +`RichDocument` wraps a `DoclingDocument` (e.g. produced by converting a PDF or +Markdown file) and renders it as Markdown for a language model. `Table` represents a +single table within a Docling document and provides `transpose`, `to_markdown`, and +query/transform helpers. Use `RichDocument.from_document_file` to convert a PDF or +other supported format, and `get_tables()` to extract structured table data for downstream LLM-driven Q&A or transformation tasks. """ @@ -31,11 +31,11 @@ class RichDocument(Component[str]): - """A ``RichDocument`` is a block of content backed by a ``DoclingDocument``. + """A `RichDocument` is a block of content backed by a `DoclingDocument`. Provides helper functions for working with the document and extracting parts - such as tables. Use ``from_document_file`` to convert PDFs or other formats, - and ``save``/``load`` for persistence. + such as tables. Use `from_document_file` to convert PDFs or other formats, + and `save`/`load` for persistence. Args: doc (DoclingDocument): The underlying Docling document to wrap. @@ -73,7 +73,7 @@ def _parse(self, computed: ModelOutputThunk) -> str: return computed.value if computed.value is not None else "" def docling(self) -> DoclingDocument: - """Return the underlying ``DoclingDocument``. + """Return the underlying `DoclingDocument`. Returns: DoclingDocument: The wrapped Docling document instance. @@ -88,12 +88,12 @@ def get_tables(self) -> list[Table]: """Return all tables found in this document. Returns: - list[Table]: A list of ``Table`` objects extracted from the document. + list[Table]: A list of `Table` objects extracted from the document. """ return [Table(x, self.docling()) for x in self.docling().tables] def save(self, filename: str | Path) -> None: - """Save the underlying ``DoclingDocument`` to a JSON file for later reuse. + """Save the underlying `DoclingDocument` to a JSON file for later reuse. Args: filename (str | Path): Destination file path for the serialized @@ -105,14 +105,14 @@ def save(self, filename: str | Path) -> None: @classmethod def load(cls, filename: str | Path) -> RichDocument: - """Load a ``RichDocument`` from a previously saved ``DoclingDocument`` JSON file. + """Load a `RichDocument` from a previously saved `DoclingDocument` JSON file. Args: filename (str | Path): Path to a JSON file previously created by - ``RichDocument.save``. + `RichDocument.save`. Returns: - RichDocument: A new ``RichDocument`` wrapping the loaded document. + RichDocument: A new `RichDocument` wrapping the loaded document. """ if type(filename) is str: filename = Path(filename) @@ -123,7 +123,7 @@ def load(cls, filename: str | Path) -> RichDocument: def from_document_file( cls, source: str | Path | DocumentStream, do_ocr: bool = True ) -> RichDocument: - """Convert a document file to a ``RichDocument`` using Docling. + """Convert a document file to a `RichDocument` using Docling. Args: source (str | Path | DocumentStream): Path or stream for the @@ -132,7 +132,7 @@ def from_document_file( text-based PDFs to avoid downloading OCR model weights. Returns: - RichDocument: A new ``RichDocument`` wrapping the converted document. + RichDocument: A new `RichDocument` wrapping the converted document. """ pipeline_options = PdfPipelineOptions( images_scale=2.0, generate_picture_images=True, do_ocr=do_ocr @@ -149,7 +149,7 @@ def from_document_file( class TableQuery(Query): - """A ``Query`` component specialised for ``Table`` objects. + """A `Query` component specialised for `Table` objects. Formats the table as Markdown alongside the query string so the LLM receives both the structured table content and the natural-language question. @@ -167,7 +167,7 @@ def parts(self) -> list[Component | CBlock]: """Return the constituent parts of this table query. Returns: - list[Component | CBlock]: A list containing the wrapped ``Table`` + list[Component | CBlock]: A list containing the wrapped `Table` object. """ cs: list[Component | CBlock] = [self._obj] @@ -196,7 +196,7 @@ def format_for_llm(self) -> TemplateRepresentation: class TableTransform(Transform): - """A ``Transform`` component specialised for ``Table`` objects. + """A `Transform` component specialised for `Table` objects. Formats the table as Markdown alongside the transformation instruction so the LLM receives both the structured table content and the mutation description. @@ -214,7 +214,7 @@ def parts(self) -> list[Component | CBlock]: """Return the constituent parts of this table transform. Returns: - list[Component | CBlock]: A list containing the wrapped ``Table`` + list[Component | CBlock]: A list containing the wrapped `Table` object. """ cs: list[Component | CBlock] = [self._obj] @@ -246,11 +246,11 @@ def format_for_llm(self) -> TemplateRepresentation: class Table(MObject): - """A ``Table`` represents a single table within a larger Docling Document. + """A `Table` represents a single table within a larger Docling Document. Args: - ti (TableItem): The Docling ``TableItem`` extracted from the document. - doc (DoclingDocument): The parent ``DoclingDocument``. Passing ``None`` + ti (TableItem): The Docling `TableItem` extracted from the document. + doc (DoclingDocument): The parent `DoclingDocument`. Passing `None` may cause downstream Docling functions to fail. """ @@ -262,7 +262,7 @@ def __init__(self, ti: TableItem, doc: DoclingDocument): @classmethod def from_markdown(cls, md: str) -> Table | None: - """Create a ``Table`` from a Markdown string by round-tripping through Docling. + """Create a `Table` from a Markdown string by round-tripping through Docling. Wraps the Markdown in a minimal document, converts it with Docling, and returns the first table found. @@ -271,8 +271,8 @@ def from_markdown(cls, md: str) -> Table | None: md (str): A Markdown string containing at least one table. Returns: - Table | None: The first ``Table`` extracted from the Markdown, or - ``None`` if no table could be found. + Table | None: The first `Table` extracted from the Markdown, or + `None` if no table could be found. """ fake_doc = f"# X\n\n{md}\n" bs = io.BytesIO(fake_doc.encode("utf-8")) @@ -286,7 +286,7 @@ def parts(self): """Return the constituent parts of this table component. The current implementation always returns an empty list because the - table is rendered entirely through ``format_for_llm``. + table is rendered entirely through `format_for_llm`. Returns: list[Component | CBlock]: Always an empty list. @@ -302,11 +302,11 @@ def to_markdown(self) -> str: return self._ti.export_to_markdown(self._doc) def transpose(self) -> Table | None: - """Transpose this table and return the result as a new ``Table``. + """Transpose this table and return the result as a new `Table`. Returns: - Table | None: A new transposed ``Table``, or ``None`` if the - transposed Markdown cannot be parsed back into a ``Table``. + Table | None: A new transposed `Table`, or `None` if the + transposed Markdown cannot be parsed back into a `Table`. """ t = self._ti.export_to_dataframe().transpose() return Table.from_markdown(t.to_markdown()) @@ -315,8 +315,8 @@ def format_for_llm(self) -> TemplateRepresentation | str: """Return the table representation for the Formatter. Returns: - TemplateRepresentation | str: A ``TemplateRepresentation`` that - renders the table as its Markdown string using a ``{{table}}`` + TemplateRepresentation | str: A `TemplateRepresentation` that + renders the table as its Markdown string using a `{{table}}` template. """ return TemplateRepresentation( diff --git a/mellea/stdlib/components/genslot.py b/mellea/stdlib/components/genslot.py index 943b6f654..7a1186a65 100644 --- a/mellea/stdlib/components/genslot.py +++ b/mellea/stdlib/components/genslot.py @@ -1,7 +1,7 @@ -"""Backward-compatibility shim — use ``mellea.stdlib.components.genstub`` instead. +"""Backward-compatibility shim — use `mellea.stdlib.components.genstub` instead. .. deprecated:: - The ``genslot`` module has been renamed to ``genstub``. This shim will be + The `genslot` module has been renamed to `genstub`. This shim will be removed in a future release. """ diff --git a/mellea/stdlib/components/genstub.py b/mellea/stdlib/components/genstub.py index 05ca11088..b836620ee 100644 --- a/mellea/stdlib/components/genstub.py +++ b/mellea/stdlib/components/genstub.py @@ -68,9 +68,9 @@ class FunctionDict(TypedDict): """Return Type for a Function Component. Attributes: - name (str): The function's ``__name__``. + name (str): The function's `__name__`. signature (str): The function's parameter signature as a string. - docstring (str | None): The function's docstring, or ``None`` if absent. + docstring (str | None): The function's docstring, or `None` if absent. """ name: str @@ -116,9 +116,9 @@ def __init__( class Arguments(CBlock): - """A ``CBlock`` that renders a list of ``Argument`` objects as human-readable text. + """A `CBlock` that renders a list of `Argument` objects as human-readable text. - Each argument is formatted as ``"- name: value (type: annotation)"`` and the + Each argument is formatted as `"- name: value (type: annotation)"` and the items are newline-joined into a single string suitable for inclusion in a prompt. Args: @@ -185,10 +185,10 @@ def __init__( class Function(Generic[P, R]): - """Wraps a callable with its introspected ``FunctionDict`` metadata. + """Wraps a callable with its introspected `FunctionDict` metadata. Stores the original callable alongside its name, signature, and docstring - as produced by ``describe_function``, so generative stubs can render them + as produced by `describe_function`, so generative stubs can render them into prompts without re-inspecting the function each time. Args: @@ -318,11 +318,11 @@ def __init__(self): class GenerativeStub(Component[R], Generic[P, R]): - """Abstract base class for AI-powered function wrappers produced by ``@generative``. + """Abstract base class for AI-powered function wrappers produced by `@generative`. - A ``GenerativeStub`` wraps a callable and uses an LLM to generate its output. - Subclasses (``SyncGenerativeStub``, ``AsyncGenerativeStub``) implement - ``__call__`` for synchronous and asynchronous invocation respectively. + A `GenerativeStub` wraps a callable and uses an LLM to generate its output. + Subclasses (`SyncGenerativeStub`, `AsyncGenerativeStub`) implement + `__call__` for synchronous and asynchronous invocation respectively. The function's signature, docstring, and type hints are rendered into a prompt so the LLM can imitate the function's intended behaviour. @@ -374,9 +374,9 @@ def extract_args_and_kwargs(*args, **kwargs) -> ExtractedArgs: Args: args: Positional arguments; the first must be either a - ``MelleaSession`` or a ``Context`` instance. + `MelleaSession` or a `Context` instance. kwargs: Keyword arguments for both the generative stub machinery - (e.g. ``m``, ``context``, ``backend``, ``requirements``) and the + (e.g. `m`, `context`, `backend`, `requirements`) and the wrapped function's own parameters. Returns: @@ -485,13 +485,13 @@ def parts(self) -> list[Component | CBlock]: def format_for_llm(self) -> TemplateRepresentation: """Format this generative stub for the language model. - Builds a ``TemplateRepresentation`` containing the function metadata + Builds a `TemplateRepresentation` containing the function metadata (name, signature, docstring), the bound arguments, and any requirement descriptions. Returns: TemplateRepresentation: The formatted representation ready for the - ``Formatter`` to render into a prompt. + `Formatter` to render into a prompt. """ return TemplateRepresentation( obj=self, @@ -524,9 +524,9 @@ def _parse(self, computed: ModelOutputThunk) -> R: class SyncGenerativeStub(GenerativeStub, Generic[P, R]): """A synchronous generative stub that blocks until the LLM response is ready. - Returned by ``@generative`` when the decorated function is not a coroutine. - ``__call__`` returns the parsed result directly (when a session is passed) or a - ``(result, context)`` tuple (when a context and backend are passed). + Returned by `@generative` when the decorated function is not a coroutine. + `__call__` returns the parsed result directly (when a session is passed) or a + `(result, context)` tuple (when a context and backend are passed). """ @overload diff --git a/mellea/stdlib/components/instruction.py b/mellea/stdlib/components/instruction.py index 30faaea20..d7cd63f4d 100644 --- a/mellea/stdlib/components/instruction.py +++ b/mellea/stdlib/components/instruction.py @@ -1,7 +1,7 @@ -"""``Instruction`` component for instruct/validate/repair loops. +"""`Instruction` component for instruct/validate/repair loops. -``Instruction`` is the primary component type used with ``MelleaSession.instruct``. It -packages a task ``description``, a list of ``Requirement`` constraints, optional +`Instruction` is the primary component type used with `MelleaSession.instruct`. It +packages a task `description`, a list of `Requirement` constraints, optional in-context-learning examples, a grounding context dict, user variables for Jinja2 template interpolation, and output/input prefix overrides into a single renderable unit. The session's sampling strategy evaluates each requirement against the model's @@ -39,7 +39,7 @@ class Instruction(Component[str]): to all string parameters. prefix (str | CBlock | None): A prefix prepended before the model's generation. output_prefix (str | CBlock | None): A prefix prepended to the model's output token - stream (currently unsupported; must be ``None``). + stream (currently unsupported; must be `None`). images (list[ImageBlock] | None): Images to include in the prompt. Attributes: @@ -217,8 +217,8 @@ def copy_and_repair(self, repair_string: str) -> Instruction: describing which requirements failed and why. Returns: - Instruction: A new ``Instruction`` identical to this one but with - ``_repair_string`` set to ``repair_string``. + Instruction: A new `Instruction` identical to this one but with + `_repair_string` set to `repair_string`. """ res = deepcopy(self) res._repair_string = repair_string diff --git a/mellea/stdlib/components/intrinsic/_util.py b/mellea/stdlib/components/intrinsic/_util.py index 94349612d..40128fded 100644 --- a/mellea/stdlib/components/intrinsic/_util.py +++ b/mellea/stdlib/components/intrinsic/_util.py @@ -19,14 +19,14 @@ def _resolve_question( question: str | None, context: ChatContext, backend: Backend | None = None ) -> tuple[str, ChatContext]: - """Return ``(question_text, context_to_use)``. + """Return `(question_text, context_to_use)`. - When *question* is not ``None``, returns it with *context* unchanged. - When ``None``, extracts the text from the last turn's ``model_input`` + When *question* is not `None`, returns it with *context* unchanged. + When `None`, extracts the text from the last turn's `model_input` and rewinds *context* to before that element. - Supports ``Message`` (via ``.content``), ``CBlock`` (via ``.value``), - and generic ``Component`` types (via ``TemplateFormatter.print()``). + Supports `Message` (via `.content`), `CBlock` (via `.value`), + and generic `Component` types (via `TemplateFormatter.print()`). """ if question is not None: return question, context @@ -71,10 +71,10 @@ def _resolve_question( def _resolve_response( response: str | None, context: ChatContext ) -> tuple[str, ChatContext]: - """Return ``(response_text, context_to_use)``. + """Return `(response_text, context_to_use)`. - When *response* is not ``None``, returns it with *context* unchanged. - When ``None``, extracts from the last turn's ``output.value`` and rewinds + When *response* is not `None`, returns it with *context* unchanged. + When `None`, extracts from the last turn's `output.value` and rewinds *context* to before that output. """ if response is not None: diff --git a/mellea/stdlib/components/intrinsic/core.py b/mellea/stdlib/components/intrinsic/core.py index 2329963e9..8f2b38083 100644 --- a/mellea/stdlib/components/intrinsic/core.py +++ b/mellea/stdlib/components/intrinsic/core.py @@ -34,7 +34,7 @@ def requirement_check( Intrinsic function that determines if the text satisfies the given requirements. The requirement text is passed through to the adapter's - ``io.yaml`` ``instruction`` template via ``IntrinsicsRewriter``, which + `io.yaml` `instruction` template via `IntrinsicsRewriter`, which appends the formatted evaluation prompt as a new user message. Args: @@ -63,27 +63,27 @@ def find_context_attributions( documents that were most important to the LLM in generating each sentence in the assistant response. - :param response: Assistant response. When ``None``, the response is extracted - from the last assistant output in ``context``. - :param documents: Documents that were used to generate ``response``. Each element - may be a ``Document`` or a plain string. Strings are wrapped in ``Document`` - with an auto-generated ``doc_id`` (``"0"``, ``"1"``, ...); for explicit - control, pass ``Document`` objects with ``doc_id`` set. ``Document`` objects - without ``doc_id`` trigger a warning because the intrinsic uses ``doc_id`` to + :param response: Assistant response. When `None`, the response is extracted + from the last assistant output in `context`. + :param documents: Documents that were used to generate `response`. Each element + may be a `Document` or a plain string. Strings are wrapped in `Document` + with an auto-generated `doc_id` (`"0"`, `"1"`, ...); for explicit + control, pass `Document` objects with `doc_id` set. `Document` objects + without `doc_id` trigger a warning because the intrinsic uses `doc_id` to identify attribution sources. :param context: Context of the dialog between user and assistant, ending with a user query :param backend: Backend that supports intrinsic adapters :return: List of records with the following fields: - * ``response_begin`` - * ``response_end`` - * ``response_text`` - * ``attribution_doc_id`` - * ``attribution_msg_index`` - * ``attribution_begin`` - * ``attribution_end`` - * ``attribution_text`` + * `response_begin` + * `response_end` + * `response_text` + * `attribution_doc_id` + * `attribution_msg_index` + * `attribution_begin` + * `attribution_end` + * `attribution_text` Begin and end offsets are character offsets into their respective UTF-8 strings. """ response, context = _resolve_response(response, context) diff --git a/mellea/stdlib/components/intrinsic/guardian.py b/mellea/stdlib/components/intrinsic/guardian.py index e66e61beb..e888ba881 100644 --- a/mellea/stdlib/components/intrinsic/guardian.py +++ b/mellea/stdlib/components/intrinsic/guardian.py @@ -1,10 +1,10 @@ """Intrinsic functions for Guardian safety and hallucination detection. -The Guardian adapters (``guardian-core``, ``policy-guardrails``, -``factuality-detection``, ``factuality-correction``) require a -````-prefixed envelope as the last user message of the request. -That envelope is built from the ``instruction:`` field of each adapter's -``io.yaml`` via :class:`IntrinsicsRewriter`; the helpers below just resolve +The Guardian adapters (`guardian-core`, `policy-guardrails`, +`factuality-detection`, `factuality-correction`) require a +``-prefixed envelope as the last user message of the request. +That envelope is built from the `instruction:` field of each adapter's +`io.yaml` via :class:`IntrinsicsRewriter`; the helpers below just resolve any convenience inputs (e.g. :data:`CRITERIA_BANK` lookups) and pass the resolved kwargs through. """ @@ -19,10 +19,10 @@ _UNSET: object = object() """Sentinel distinguishing 'caller omitted scoring_schema' from 'caller passed the default value explicitly'. Used only to detect conflicts with the -deprecated ``target_role`` kwarg.""" +deprecated `target_role` kwarg.""" _TARGET_ROLE_TO_SCHEMA = {"user": "user_prompt", "assistant": "assistant_response"} -"""Mapping used by the deprecated ``target_role`` path of :func:`guardian_check`.""" +"""Mapping used by the deprecated `target_role` path of :func:`guardian_check`.""" def policy_guardrails( @@ -31,7 +31,7 @@ def policy_guardrails( """Checks whether text complied with specified policy. Uses the policy_guardrails LoRA adapter to judge whether the scenario - described in the last message in ``context`` is compliant with the given ``policy_text``. + described in the last message in `context` is compliant with the given `policy_text`. :param context: Chat context containing the conversation to evaluate. :param backend: Backend instance that supports LoRA adapters. @@ -75,10 +75,10 @@ def policy_guardrails( } """Pre-baked scoring-schema phrasings for :func:`guardian_check`. -Keys can be passed directly as the ``scoring_schema`` parameter; any +Keys can be passed directly as the `scoring_schema` parameter; any other string is used verbatim. Entries must resolve to a yes/no -verdict — the adapter's ``io.yaml`` constrains output to ``"yes"`` or -``"no"``, so a schema like ``'return "safe" or "unsafe"'`` will be +verdict — the adapter's `io.yaml` constrains output to `"yes"` or +`"no"`, so a schema like `'return "safe" or "unsafe"'` will be coerced to yes/no by constrained decoding. """ @@ -155,7 +155,7 @@ def policy_guardrails( } """Pre-baked criteria definitions from the Granite Guardian model card. -Keys can be passed directly to :func:`guardian_check` as the ``criteria`` +Keys can be passed directly to :func:`guardian_check` as the `criteria` parameter. """ @@ -170,26 +170,26 @@ def guardian_check( """Check whether text meets specified safety/quality criteria. Uses the guardian-core LoRA adapter to judge whether the span - identified by ``scoring_schema`` in ``context`` meets the given + identified by `scoring_schema` in `context` meets the given criteria. Args: context: Chat context containing the conversation to evaluate. backend: Backend instance that supports LoRA adapters. criteria: Description of the criteria to check against. Can be a - key from :data:`CRITERIA_BANK` (e.g. ``"harm"``) or a custom + key from :data:`CRITERIA_BANK` (e.g. `"harm"`) or a custom criteria string. scoring_schema: Sentence that tells the judge which span to evaluate and how to decide. Can be a key from - :data:`SCORING_SCHEMA_BANK` (e.g. ``"user_prompt"``) or a - custom string. Defaults to ``"assistant_response"``. Must + :data:`SCORING_SCHEMA_BANK` (e.g. `"user_prompt"`) or a + custom string. Defaults to `"assistant_response"`. Must still resolve to a yes/no verdict — the adapter's - ``response_format`` constrains output to ``"yes"``/``"no"``. + `response_format` constrains output to `"yes"`/`"no"`. target_role: Deprecated. Role whose last message is being - evaluated (``"user"`` or ``"assistant"``). Prefer - ``scoring_schema`` with a key from + evaluated (`"user"` or `"assistant"`). Prefer + `scoring_schema` with a key from :data:`SCORING_SCHEMA_BANK`. Passing both - ``scoring_schema`` and ``target_role`` raises + `scoring_schema` and `target_role` raises :class:`TypeError`. Returns: diff --git a/mellea/stdlib/components/intrinsic/intrinsic.py b/mellea/stdlib/components/intrinsic/intrinsic.py index 1da1cabd1..484ef9696 100644 --- a/mellea/stdlib/components/intrinsic/intrinsic.py +++ b/mellea/stdlib/components/intrinsic/intrinsic.py @@ -1,10 +1,10 @@ -"""``Intrinsic`` component for invoking fine-tuned adapter capabilities. +"""`Intrinsic` component for invoking fine-tuned adapter capabilities. -An ``Intrinsic`` component references a named adapter from Mellea's intrinsic catalog +An `Intrinsic` component references a named adapter from Mellea's intrinsic catalog and transforms a chat completion request — typically by injecting new messages, modifying model parameters, or applying structured output constraints. It must be -paired with a backend that supports adapter loading (e.g. ``LocalHFBackend`` with an -attached ``IntrinsicAdapter``). +paired with a backend that supports adapter loading (e.g. `LocalHFBackend` with an +attached `IntrinsicAdapter`). """ from ....backends.adapters import AdapterType, fetch_intrinsic_metadata @@ -71,9 +71,9 @@ def parts(self) -> list[Component | CBlock]: return [] # TODO revisit this. def format_for_llm(self) -> TemplateRepresentation | str: - """Not implemented for the base ``Intrinsic`` class. + """Not implemented for the base `Intrinsic` class. - ``Intrinsic`` components are intended to be used as the *action* passed + `Intrinsic` components are intended to be used as the *action* passed directly to the backend, not as a part of the context rendered by the formatter. @@ -81,8 +81,8 @@ def format_for_llm(self) -> TemplateRepresentation | str: TemplateRepresentation | str: Never returns; always raises. Raises: - NotImplementedError: Always, because ``Intrinsic`` does not - implement ``format_for_llm`` by default. + NotImplementedError: Always, because `Intrinsic` does not + implement `format_for_llm` by default. """ raise NotImplementedError( "`Intrinsic` doesn't implement format_for_llm by default. You should only " diff --git a/mellea/stdlib/components/intrinsic/rag.py b/mellea/stdlib/components/intrinsic/rag.py index 4dd20a1c8..27ad67dd0 100644 --- a/mellea/stdlib/components/intrinsic/rag.py +++ b/mellea/stdlib/components/intrinsic/rag.py @@ -23,11 +23,11 @@ def check_answerability( Args: question: Question that the user has posed in response to the last turn in - ``context``. When ``None``, the question is extracted from the last - user message in ``context``. + `context`. When `None`, the question is extracted from the last + user message in `context`. documents: Document snippets retrieved that may or may not answer the - indicated question. Each element may be a ``Document`` or a plain - string (automatically wrapped in ``Document``). + indicated question. Each element may be a `Document` or a plain + string (automatically wrapped in `Document`). context: Chat context containing the conversation thus far. backend: Backend instance that supports adding the LoRA or aLoRA adapters for answerability checks. @@ -56,13 +56,13 @@ def rewrite_question( Args: question: Question that the user has posed in response to the last turn in - ``context``. When ``None``, the question is extracted from the last - user message in ``context``. + `context`. When `None`, the question is extracted from the last + user message in `context`. context: Chat context containing the conversation thus far. backend: Backend instance that supports adding the LoRA or aLoRA adapters. Returns: - Rewritten version of ``question``. + Rewritten version of `question`. """ question, context = _resolve_question(question, context, backend) result_json = call_intrinsic( @@ -84,11 +84,11 @@ def clarify_query( appropriate clarification question if needed. Args: - question: Question that the user has posed. When ``None``, the question - is extracted from the last user message in ``context``. + question: Question that the user has posed. When `None`, the question + is extracted from the last user message in `context`. documents: Document snippets retrieved for the question. Each element - may be a ``Document`` or a plain string (automatically wrapped in - ``Document``). + may be a `Document` or a plain string (automatically wrapped in + `Document`). context: Chat context containing the conversation thus far. backend: Backend instance that supports the adapters that implement this intrinsic. @@ -120,23 +120,23 @@ def find_citations( in a potential assistant response to a user question. Args: - response: Potential assistant response. When ``None``, the response is - extracted from the last assistant output in ``context``. - documents: Documents that were used to generate ``response``. Each element - may be a ``Document`` or a plain string. Strings are wrapped in - ``Document`` with an auto-generated ``doc_id`` (``"0"``, ``"1"``, ...); - for explicit control, pass ``Document`` objects with ``doc_id`` set. - ``Document`` objects without ``doc_id`` trigger a warning because the - intrinsic uses ``doc_id`` to identify citation sources. + response: Potential assistant response. When `None`, the response is + extracted from the last assistant output in `context`. + documents: Documents that were used to generate `response`. Each element + may be a `Document` or a plain string. Strings are wrapped in + `Document` with an auto-generated `doc_id` (`"0"`, `"1"`, ...); + for explicit control, pass `Document` objects with `doc_id` set. + `Document` objects without `doc_id` trigger a warning because the + intrinsic uses `doc_id` to identify citation sources. context: Context of the dialog between user and assistant at the point where the user has just asked a question that will be answered with RAG documents. backend: Backend that supports one of the adapters that implements this intrinsic. Returns: - List of records with the following fields: ``response_begin``, - ``response_end``, ``response_text``, ``citation_doc_id``, ``citation_begin``, - ``citation_end``, ``citation_text``. Begin and end offsets are character + List of records with the following fields: `response_begin`, + `response_end`, `response_text`, `citation_doc_id`, `citation_begin`, + `citation_end`, `citation_text`. Begin and end offsets are character offsets into their respective UTF-8 strings. """ response, context = _resolve_response(response, context) @@ -167,10 +167,10 @@ def check_context_relevance( question was asked. Args: - question: Question that the user has posed. When ``None``, the question - is extracted from the last user message in ``context``. - document: A retrieved document snippet. May be a ``Document`` or a plain - string (automatically wrapped in ``Document``). + question: Question that the user has posed. When `None`, the question + is extracted from the last user message in `context`. + document: A retrieved document snippet. May be a `Document` or a plain + string (automatically wrapped in `Document`). context: The chat up to the point where the user asked a question. backend: Backend instance that supports the adapters that implement this intrinsic. @@ -207,19 +207,19 @@ def flag_hallucinated_content( Args: response: The assistant's response to the user's question in the last turn - of ``context``. When ``None``, the response is extracted from the last - assistant output in ``context``. - documents: Document snippets that were used to generate ``response``. Each - element may be a ``Document`` or a plain string (automatically wrapped - in ``Document``). + of `context`. When `None`, the response is extracted from the last + assistant output in `context`. + documents: Document snippets that were used to generate `response`. Each + element may be a `Document` or a plain string (automatically wrapped + in `Document`). context: A chat log that ends with a user asking a question. backend: Backend instance that supports the adapters that implement this intrinsic. Returns: - List of records with the following fields: ``response_begin``, - ``response_end``, ``response_text``, ``faithfulness``, - ``explanation``. + List of records with the following fields: `response_begin`, + `response_end`, `response_text`, `faithfulness`, + `explanation`. """ response, context = _resolve_response(response, context) result_json = call_intrinsic( diff --git a/mellea/stdlib/components/mify.py b/mellea/stdlib/components/mify.py index ec8df5c6d..b57b707b5 100644 --- a/mellea/stdlib/components/mify.py +++ b/mellea/stdlib/components/mify.py @@ -1,10 +1,10 @@ -"""The ``@mify`` decorator for turning Python objects into ``Component``s. +"""The `@mify` decorator for turning Python objects into `Component`s. -``mify`` wraps an existing Python class or instance with the ``MifiedProtocol`` +`mify` wraps an existing Python class or instance with the `MifiedProtocol` interface, exposing its fields as named spans and its documented methods as -``MelleaTool`` instances callable by the LLM. The resulting ``MifiedProtocol`` object +`MelleaTool` instances callable by the LLM. The resulting `MifiedProtocol` object can be queried, transformed, and formatted for a language model without any manual -``Component`` subclassing. Use ``mify`` when you have an existing domain object +`Component` subclassing. Use `mify` when you have an existing domain object (dataclass, Pydantic model, or plain class) that you want to expose directly to an LLM-driven pipeline. """ @@ -66,7 +66,7 @@ def get_query_object(self, query: str) -> Query: query (str): The natural-language query string. Returns: - Query: A ``Query`` component wrapping this object and the given query. + Query: A `Query` component wrapping this object and the given query. """ return self._query_type(self, query) @@ -79,7 +79,7 @@ def get_transform_object(self, transformation: str) -> Transform: transformation (str): The natural-language transformation description. Returns: - Transform: A ``Transform`` component wrapping this object and the + Transform: A `Transform` component wrapping this object and the given transformation description. """ return self._transform_type(self, transformation) @@ -89,8 +89,8 @@ def content_as_string(self) -> str: [no-index] - Delegates to the ``stringify_func`` passed to ``mify`` when one was - provided; otherwise falls back to ``str(self)``. + Delegates to the `stringify_func` passed to `mify` when one was + provided; otherwise falls back to `str(self)`. Returns: str: String representation of the mified object's content. @@ -193,14 +193,14 @@ def _get_all_fields(self) -> dict[str, Any]: return narrowed def format_for_llm(self) -> TemplateRepresentation: - """Return the ``TemplateRepresentation`` for this mified object. + """Return the `TemplateRepresentation` for this mified object. [no-index] - Sets the ``TemplateRepresentation`` fields based on the object and the - configuration values supplied to ``mify`` (fields, templates, tools, etc.). + Sets the `TemplateRepresentation` fields based on the object and the + configuration values supplied to `mify` (fields, templates, tools, etc.). - See the ``mify`` decorator for more details. + See the `mify` decorator for more details. Returns: TemplateRepresentation: The formatted representation including args, @@ -243,17 +243,17 @@ def parse(self, computed: ModelOutputThunk) -> str: [no-index] - Delegates to ``_parse`` and wraps any exception in a - ``ComponentParseError`` to give callers a consistent error type. + Delegates to `_parse` and wraps any exception in a + `ComponentParseError` to give callers a consistent error type. Args: computed (ModelOutputThunk): The raw model output to parse. Returns: - str: The string value extracted from ``computed``. + str: The string value extracted from `computed`. Raises: - ComponentParseError: If ``_parse`` raises any exception during + ComponentParseError: If `_parse` raises any exception during parsing. """ try: @@ -316,24 +316,24 @@ def mify(*args, **kwargs) -> object: # noqa: D417 Args: obj: A class or an instance of a class to mify. Omit when using as a - decorator with arguments (e.g. ``@mify(fields_include={...})``). + decorator with arguments (e.g. `@mify(fields_include={...})`). query_type: A specific query component type to use when querying a model. - Defaults to ``Query``. + Defaults to `Query`. transform_type: A specific transform component type to use when - transforming with a model. Defaults to ``Transform``. + transforming with a model. Defaults to `Transform`. fields_include: Fields of the object to include in its representation to - models. When set, ``stringify_func`` is not used. + models. When set, `stringify_func` is not used. fields_exclude: Fields of the object to exclude from its representation to models. funcs_include: Functions of the object to expose as tools to models. funcs_exclude: Functions of the object to hide from models. template: A Jinja2 template string. Takes precedence over - ``template_order`` when provided. + `template_order` when provided. template_order: A template name or list of names used when searching for applicable templates. parsing_func: Not yet implemented. stringify_func: A callable used to create a string representation of the - object for ``content_as_string``. + object for `content_as_string`. Returns: An object if an object was passed in or a decorator (callable) to mify classes. diff --git a/mellea/stdlib/components/mobject.py b/mellea/stdlib/components/mobject.py index e706b80d5..03413ebcc 100644 --- a/mellea/stdlib/components/mobject.py +++ b/mellea/stdlib/components/mobject.py @@ -1,10 +1,10 @@ -"""``MObject``, ``Query``, ``Transform``, and ``MObjectProtocol`` for query/transform workflows. +"""`MObject`, `Query`, `Transform`, and `MObjectProtocol` for query/transform workflows. -Defines the ``MObjectProtocol`` protocol for objects that can be queried and -transformed by an LLM, and the concrete ``MObject`` base class that implements it. -Also provides the ``Query`` and ``Transform`` ``Component`` subtypes, which wrap an +Defines the `MObjectProtocol` protocol for objects that can be queried and +transformed by an LLM, and the concrete `MObject` base class that implements it. +Also provides the `Query` and `Transform` `Component` subtypes, which wrap an object with a natural-language question or mutation instruction respectively. These -primitives underpin ``@mify`` and can be composed directly to build document Q&A +primitives underpin `@mify` and can be composed directly to build document Q&A or structured extraction pipelines. """ @@ -19,9 +19,9 @@ class Query(Component[str]): - """A ``Component`` that pairs an ``MObject`` with a natural-language question. + """A `Component` that pairs an `MObject` with a natural-language question. - Wraps the object and its query string into a ``TemplateRepresentation`` so the + Wraps the object and its query string into a `TemplateRepresentation` so the formatter can render both together in a prompt, optionally forwarding the object's tools and fields to the template. @@ -47,7 +47,7 @@ def format_for_llm(self) -> TemplateRepresentation | str: """Format this query for the language model. Returns: - TemplateRepresentation | str: A ``TemplateRepresentation`` containing + TemplateRepresentation | str: A `TemplateRepresentation` containing the query string, the wrapped object, and any tools or fields from the object's own representation. """ @@ -77,10 +77,10 @@ def _parse(self, computed: ModelOutputThunk) -> str: class Transform(Component[str]): - """A ``Component`` that pairs an ``MObject`` with a natural-language mutation instruction. + """A `Component` that pairs an `MObject` with a natural-language mutation instruction. Wraps the object and its transformation description into a - ``TemplateRepresentation`` so the formatter can render both together in a prompt, + `TemplateRepresentation` so the formatter can render both together in a prompt, optionally forwarding the object's tools and fields to the template. Args: @@ -105,7 +105,7 @@ def format_for_llm(self) -> TemplateRepresentation | str: """Format this transform for the language model. Returns: - TemplateRepresentation | str: A ``TemplateRepresentation`` containing + TemplateRepresentation | str: A `TemplateRepresentation` containing the transformation description, the wrapped object, and any tools or fields from the object's own representation. """ @@ -153,7 +153,7 @@ def get_query_object(self, query: str) -> Query: query (str): The query string. Returns: - Query: A ``Query`` component wrapping this object and the given + Query: A `Query` component wrapping this object and the given query string. """ ... @@ -165,7 +165,7 @@ def get_transform_object(self, transformation: str) -> Transform: transformation (str): The transformation description string. Returns: - Transform: A ``Transform`` component wrapping this object and the + Transform: A `Transform` component wrapping this object and the given transformation description. """ ... @@ -173,7 +173,7 @@ def get_transform_object(self, transformation: str) -> Transform: def content_as_string(self) -> str: """Return the content of this MObject as a plain string. - The default value is just ``str(self)``. + The default value is just `str(self)`. Subclasses should override this method. Returns: @@ -184,7 +184,7 @@ def content_as_string(self) -> str: def _get_all_members(self) -> dict[str, Callable]: """Return all methods from this MObject that are not inherited from the superclass. - Undocumented methods and methods with ``[no-index]`` in their docstring + Undocumented methods and methods with `[no-index]` in their docstring are ignored. """ ... @@ -192,8 +192,8 @@ def _get_all_members(self) -> dict[str, Callable]: def format_for_llm(self) -> TemplateRepresentation | str: """Return the template representation used by the formatter. - The default ``TemplateRepresentation`` uses automatic parsing for tools - and fields. Content is retrieved from ``content_as_string()``. + The default `TemplateRepresentation` uses automatic parsing for tools + and fields. Content is retrieved from `content_as_string()`. Returns: TemplateRepresentation | str: The formatted representation for the @@ -207,13 +207,13 @@ def _parse(self, computed: ModelOutputThunk) -> str: class MObject(Component[str]): - """An extension of ``Component`` for adding query and transform operations. + """An extension of `Component` for adding query and transform operations. Args: - query_type (type): The ``Query`` subclass to use when constructing query - components. Defaults to ``Query``. - transform_type (type): The ``Transform`` subclass to use when constructing - transform components. Defaults to ``Transform``. + query_type (type): The `Query` subclass to use when constructing query + components. Defaults to `Query`. + transform_type (type): The `Transform` subclass to use when constructing + transform components. Defaults to `Transform`. """ def __init__( @@ -238,7 +238,7 @@ def get_query_object(self, query: str) -> Query: query (str): The query string. Returns: - Query: A ``Query`` component wrapping this object and the given + Query: A `Query` component wrapping this object and the given query string. """ return self._query_type(self, query) @@ -250,7 +250,7 @@ def get_transform_object(self, transformation: str) -> Transform: transformation (str): The transformation description string. Returns: - Transform: A ``Transform`` component wrapping this object and the + Transform: A `Transform` component wrapping this object and the given transformation description. """ return self._transform_type(self, transformation) @@ -258,7 +258,7 @@ def get_transform_object(self, transformation: str) -> Transform: def content_as_string(self) -> str: """Return the content of this MObject as a plain string. - The default value is just ``str(self)``. + The default value is just `str(self)`. Subclasses should override this method. Returns: @@ -269,7 +269,7 @@ def content_as_string(self) -> str: def _get_all_members(self) -> dict[str, Callable]: """Return all methods from this MObject except methods of the superclass. - Undocumented methods and methods with ``[no-index]`` in their docstring + Undocumented methods and methods with `[no-index]` in their docstring are ignored. """ all_members: dict[str, Callable] = dict( @@ -293,8 +293,8 @@ def _get_all_members(self) -> dict[str, Callable]: def format_for_llm(self) -> TemplateRepresentation | str: """Return the template representation used by the formatter. - The default ``TemplateRepresentation`` uses automatic parsing for tools - and fields. Content is retrieved from ``content_as_string()``. + The default `TemplateRepresentation` uses automatic parsing for tools + and fields. Content is retrieved from `content_as_string()`. Returns: TemplateRepresentation | str: The formatted representation for the diff --git a/mellea/stdlib/components/react.py b/mellea/stdlib/components/react.py index 8f08fe8a0..b94e61ab6 100644 --- a/mellea/stdlib/components/react.py +++ b/mellea/stdlib/components/react.py @@ -1,10 +1,10 @@ """Components that implement the ReACT (Reason + Act) agentic pattern. -Provides ``ReactInitiator``, which primes the model with a goal and a tool list, and -``ReactThought``, which signals a thinking step. Also exports the -``MELLEA_FINALIZER_TOOL`` sentinel string used to signal loop termination. These -components are consumed by ``mellea.stdlib.frameworks.react``, which orchestrates the -reasoning-acting cycle until the model invokes ``final_answer`` or the step budget +Provides `ReactInitiator`, which primes the model with a goal and a tool list, and +`ReactThought`, which signals a thinking step. Also exports the +`MELLEA_FINALIZER_TOOL` sentinel string used to signal loop termination. These +components are consumed by `mellea.stdlib.frameworks.react`, which orchestrates the +reasoning-acting cycle until the model invokes `final_answer` or the step budget is exhausted. """ @@ -38,7 +38,7 @@ class ReactInitiator(Component[str]): Args: goal (str): The objective of the react loop. tools (list[AbstractMelleaTool] | None): Tools available to the agent. - ``None`` is treated as an empty list. + `None` is treated as an empty list. Attributes: goal (CBlock): The objective of the react loop wrapped as a content block. @@ -99,7 +99,7 @@ def __init__(self): def parts(self) -> list[Component | CBlock]: """Return the constituent parts of this component. - ``ReactThought`` has no sub-components; it solely triggers a thinking step. + `ReactThought` has no sub-components; it solely triggers a thinking step. Returns: list[Component | CBlock]: Always an empty list. diff --git a/mellea/stdlib/components/simple.py b/mellea/stdlib/components/simple.py index 09bb46a2b..98a18f8d5 100644 --- a/mellea/stdlib/components/simple.py +++ b/mellea/stdlib/components/simple.py @@ -1,9 +1,9 @@ -"""``SimpleComponent``: a lightweight named-span component. +"""`SimpleComponent`: a lightweight named-span component. -``SimpleComponent`` accepts arbitrary keyword arguments (strings, ``CBlock``s, or -``Component``s) and renders them as a JSON object keyed by the argument names. It is +`SimpleComponent` accepts arbitrary keyword arguments (strings, `CBlock`s, or +`Component`s) and renders them as a JSON object keyed by the argument names. It is the go-to component type for ad-hoc prompts that do not require a dedicated -``Component`` subclass or a Jinja2 template. +`Component` subclass or a Jinja2 template. """ from typing import Any @@ -41,11 +41,11 @@ def _kwargs_type_check(self, kwargs: dict[str, Any]) -> bool: @staticmethod def make_simple_string(kwargs: dict[str, Any]) -> str: - """Render keyword arguments as ``<|key|>value`` tagged strings. + """Render keyword arguments as `<|key|>value` tagged strings. Args: - kwargs (dict[str, Any]): Mapping of span names to their ``CBlock`` or - ``Component`` values. + kwargs (dict[str, Any]): Mapping of span names to their `CBlock` or + `Component` values. Returns: str: Newline-joined tagged representation of all keyword arguments. @@ -58,13 +58,13 @@ def make_simple_string(kwargs: dict[str, Any]) -> str: def make_json_string(kwargs: dict[str, Any]) -> str: """Serialize keyword arguments to a JSON string. - Each value is converted to its string representation: ``CBlock`` and - ``ModelOutputThunk`` values use their ``.value`` attribute, while - ``Component`` values use ``format_for_llm()``. + Each value is converted to its string representation: `CBlock` and + `ModelOutputThunk` values use their `.value` attribute, while + `Component` values use `format_for_llm()`. Args: - kwargs (dict[str, Any]): Mapping of span names to ``CBlock``, ``Component``, - or ``ModelOutputThunk`` values. + kwargs (dict[str, Any]): Mapping of span names to `CBlock`, `Component`, + or `ModelOutputThunk` values. Returns: str: JSON-encoded representation of the keyword arguments. @@ -83,7 +83,7 @@ def make_json_string(kwargs: dict[str, Any]) -> str: def format_for_llm(self) -> str: """Format this component as a JSON string representation for the language model. - Delegates to ``make_json_string`` using the stored keyword arguments. + Delegates to `make_json_string` using the stored keyword arguments. Returns: str: JSON-encoded string of all named spans in this component. diff --git a/mellea/stdlib/components/unit_test_eval.py b/mellea/stdlib/components/unit_test_eval.py index 431670d91..626bb1edd 100644 --- a/mellea/stdlib/components/unit_test_eval.py +++ b/mellea/stdlib/components/unit_test_eval.py @@ -13,8 +13,8 @@ class Message(BaseModel): """Schema for a message in the test data. Attributes: - role (str): The role of the message sender (e.g. ``"user"`` or - ``"assistant"``). + role (str): The role of the message sender (e.g. `"user"` or + `"assistant"`). content (str): The text content of the message. """ @@ -59,7 +59,7 @@ def validate_examples(cls, v: list[Example]) -> list[Example]: """Validate that the examples list is not empty. Args: - v (list[Example]): The value of the ``examples`` field being + v (list[Example]): The value of the `examples` field being validated. Returns: @@ -82,7 +82,7 @@ class TestBasedEval(Component[str]): instructions (str): Evaluation guidelines used by the judge model. inputs (list[str]): The input texts for each example. targets (list[list[str]] | None): Expected target strings for each - input. ``None`` is treated as an empty list. + input. `None` is treated as an empty list. test_id (str | None): Optional unique identifier for this test. input_ids (list[str] | None): Optional identifiers for each input. @@ -112,7 +112,7 @@ def parts(self) -> list[Component | CBlock]: Returns: list[Component | CBlock]: Always an empty list; the component - renders entirely via ``format_for_llm``. + renders entirely via `format_for_llm`. """ return [] @@ -122,7 +122,7 @@ def format_for_llm(self) -> TemplateRepresentation: Returns: TemplateRepresentation: A template representation containing the judge context (input, prediction, target, guidelines) set by - ``set_judge_context``, or an empty args dict if no context has + `set_judge_context`, or an empty args dict if no context has been set yet. """ return TemplateRepresentation( @@ -144,7 +144,7 @@ def set_judge_context( input_text (str): The original input text shown to the model. prediction (str): The model's generated output to evaluate. targets_for_input (list[str]): Reference target strings for this - input. An empty list results in ``"N/A"`` as the target text. + input. An empty list results in `"N/A"` as the target text. """ if len(targets_for_input) == 0: # no reference target_text = "N/A" @@ -164,19 +164,19 @@ def set_judge_context( @classmethod def from_json_file(cls, filepath: str) -> list["TestBasedEval"]: - """Load test evaluations from a JSON file, returning one ``TestBasedEval`` per unit test. + """Load test evaluations from a JSON file, returning one `TestBasedEval` per unit test. Args: filepath (str): Path to a JSON file containing one test-data object or a JSON array of test-data objects. Returns: - list[TestBasedEval]: A list of ``TestBasedEval`` instances, one for + list[TestBasedEval]: A list of `TestBasedEval` instances, one for each object found in the file. Raises: ValueError: If any test-data object in the file does not conform to - the ``TestData`` schema. + the `TestData` schema. """ path = Path(filepath) diff --git a/mellea/stdlib/context.py b/mellea/stdlib/context.py index f0e2e8b2f..b8a748fab 100644 --- a/mellea/stdlib/context.py +++ b/mellea/stdlib/context.py @@ -1,9 +1,9 @@ -"""Concrete ``Context`` implementations for common conversation patterns. +"""Concrete `Context` implementations for common conversation patterns. -Provides ``ChatContext``, which accumulates all turns in a sliding-window chat history -(configurable via ``window_size``), and ``SimpleContext``, in which each interaction +Provides `ChatContext`, which accumulates all turns in a sliding-window chat history +(configurable via `window_size`), and `SimpleContext`, in which each interaction is treated as a stateless single-turn exchange (no prior history is passed to the -model). Import ``ChatContext`` for multi-turn conversations and ``SimpleContext`` when +model). Import `ChatContext` for multi-turn conversations and `SimpleContext` when you want each call to the model to be independent. """ @@ -18,7 +18,7 @@ class ChatContext(Context): Args: window_size (int | None): Maximum number of context turns to include when - calling ``view_for_generation``. ``None`` (the default) means the full + calling `view_for_generation`. `None` (the default) means the full history is always returned. """ @@ -34,8 +34,8 @@ def add(self, c: Component | CBlock) -> ChatContext: c (Component | CBlock): The component or content block to append. Returns: - ChatContext: A new ``ChatContext`` with the added entry, preserving the - current ``window_size`` setting. + ChatContext: A new `ChatContext` with the added entry, preserving the + current `window_size` setting. """ new = ChatContext.from_previous(self, c) new._window_size = self._window_size @@ -44,13 +44,13 @@ def add(self, c: Component | CBlock) -> ChatContext: def view_for_generation(self) -> list[Component | CBlock] | None: """Return the context entries to pass to the model, respecting the configured window. - Uses the ``window_size`` set during initialisation to limit how many past - turns are included. ``None`` is returned when the underlying history is + Uses the `window_size` set during initialisation to limit how many past + turns are included. `None` is returned when the underlying history is non-linear. Returns: list[Component | CBlock] | None: Ordered list of context entries up to - ``window_size`` turns, or ``None`` if the history is non-linear. + `window_size` turns, or `None` if the history is non-linear. """ return self.as_list(self._window_size) @@ -65,13 +65,13 @@ def add(self, c: Component | CBlock) -> SimpleContext: c (Component | CBlock): The component or content block to record. Returns: - SimpleContext: A new ``SimpleContext`` containing only the added entry; + SimpleContext: A new `SimpleContext` containing only the added entry; prior history is not retained. """ return SimpleContext.from_previous(self, c) def view_for_generation(self) -> list[Component | CBlock] | None: - """Return an empty list, since ``SimpleContext`` does not pass history to the model. + """Return an empty list, since `SimpleContext` does not pass history to the model. Each call to the model is treated as a stateless, independent exchange. No prior turns are forwarded. diff --git a/mellea/stdlib/frameworks/react.py b/mellea/stdlib/frameworks/react.py index c39338990..9b523be58 100644 --- a/mellea/stdlib/frameworks/react.py +++ b/mellea/stdlib/frameworks/react.py @@ -1,10 +1,10 @@ """ReACT (Reason + Act) agentic pattern implementation. -Provides the ``react()`` async function, which drives a tool-use loop: the model +Provides the `react()` async function, which drives a tool-use loop: the model reasons about a goal, selects a tool, receives the result as an observation, and -repeats until it calls ``final_answer`` or the ``loop_budget`` is exhausted. Accepts -any list of ``AbstractMelleaTool`` instances and a ``ChatContext`` for multi-turn -history tracking. Raises ``RuntimeError`` if the loop ends without a final answer. +repeats until it calls `final_answer` or the `loop_budget` is exhausted. Accepts +any list of `AbstractMelleaTool` instances and a `ChatContext` for multi-turn +history tracking. Raises `RuntimeError` if the loop ends without a final answer. """ # from PIL import Image as PILImage diff --git a/mellea/stdlib/functional.py b/mellea/stdlib/functional.py index 353918aac..7c91eac34 100644 --- a/mellea/stdlib/functional.py +++ b/mellea/stdlib/functional.py @@ -264,17 +264,17 @@ def chat( content: The message text to send. context: The current conversation context. backend: The backend used to generate the response. - role: The role for the outgoing message (default ``"user"``). + role: The role for the outgoing message (default `"user"`). images: Optional list of images to include in the message. documents: Optional documents to attach to the message. Each element - may be a string or a ``Document`` object. - user_variables: Optional Jinja variable substitutions applied to ``content``. + may be a string or a `Document` object. + user_variables: Optional Jinja variable substitutions applied to `content`. format: Optional Pydantic model for constrained decoding of the response. model_options: Additional model options to merge with backend defaults. tool_calls: If true, tool calling is enabled. Returns: - Tuple of the assistant ``Message`` and the updated ``Context``. + Tuple of the assistant `Message` and the updated `Context`. """ if user_variables is not None: content_resolved = Instruction.apply_user_dict_from_jinja( @@ -318,17 +318,17 @@ def validate( """Validates a set of requirements over the output (if provided) or the current context (if the output is not provided). Args: - reqs: A single ``Requirement`` or a list of them to validate. + reqs: A single `Requirement` or a list of them to validate. context: The current conversation context. backend: The backend used for LLM-as-a-judge requirements. - output: Optional model output ``CBlock`` to validate against instead of the context. + output: Optional model output `CBlock` to validate against instead of the context. format: Optional Pydantic model for constrained decoding. model_options: Additional model options to merge with backend defaults. generate_logs: Optional list to append generation logs to. - input: Optional input ``CBlock`` to include alongside ``output`` when validating. + input: Optional input `CBlock` to include alongside `output` when validating. Returns: - List of ``ValidationResult`` objects, one per requirement. + List of `ValidationResult` objects, one per requirement. """ # Run everything in the specific event loop for this session. @@ -940,17 +940,17 @@ async def achat( content: The message text to send. context: The current conversation context. backend: The backend used to generate the response. - role: The role for the outgoing message (default ``"user"``). + role: The role for the outgoing message (default `"user"`). images: Optional list of images to include in the message. documents: Optional documents to attach to the message. Each element - may be a string or a ``Document`` object. - user_variables: Optional Jinja variable substitutions applied to ``content``. + may be a string or a `Document` object. + user_variables: Optional Jinja variable substitutions applied to `content`. format: Optional Pydantic model for constrained decoding of the response. model_options: Additional model options to merge with backend defaults. tool_calls: If true, tool calling is enabled. Returns: - Tuple of the assistant ``Message`` and the updated ``Context``. + Tuple of the assistant `Message` and the updated `Context`. """ if user_variables is not None: content_resolved = Instruction.apply_user_dict_from_jinja( @@ -994,17 +994,17 @@ async def avalidate( """Asynchronous version of .validate; validates a set of requirements over the output (if provided) or the current context (if the output is not provided). Args: - reqs: A single ``Requirement`` or a list of them to validate. + reqs: A single `Requirement` or a list of them to validate. context: The current conversation context. backend: The backend used for LLM-as-a-judge requirements. - output: Optional model output ``CBlock`` to validate against instead of the context. + output: Optional model output `CBlock` to validate against instead of the context. format: Optional Pydantic model for constrained decoding. model_options: Additional model options to merge with backend defaults. generate_logs: Optional list to append generation logs to. - input: Optional input ``CBlock`` to include alongside ``output`` when validating. + input: Optional input `CBlock` to include alongside `output` when validating. Returns: - List of ``ValidationResult`` objects, one per requirement. + List of `ValidationResult` objects, one per requirement. """ # Turn a solitary requirement in to a list of requirements, and then reqify if needed. reqs = [reqs] if not isinstance(reqs, list) else reqs diff --git a/mellea/stdlib/requirements/md.py b/mellea/stdlib/requirements/md.py index 5d21223c3..f1da8b610 100644 --- a/mellea/stdlib/requirements/md.py +++ b/mellea/stdlib/requirements/md.py @@ -32,7 +32,7 @@ def as_markdown_list(ctx: Context) -> list[str] | None: Returns: List of rendered list-item strings if the output is a markdown list, - or ``None`` if parsing fails or the output is not a list. + or `None` if parsing fails or the output is not a list. """ mistletoe = _get_mistletoe() xs = list() diff --git a/mellea/stdlib/requirements/python_reqs.py b/mellea/stdlib/requirements/python_reqs.py index 3152acb71..5f01581a7 100644 --- a/mellea/stdlib/requirements/python_reqs.py +++ b/mellea/stdlib/requirements/python_reqs.py @@ -69,11 +69,11 @@ def _has_python_code_listing(ctx: Context) -> ValidationResult: # Look for code blocks with python specifier import re - # Pattern for ```python ... ``` blocks - python_blocks = re.findall(r"```python\s*\n(.*?)\n```", content, re.DOTALL) + # Pattern for ``python ... `` blocks + python_blocks = re.findall(r"``python\s*\n(.*?)\n``", content, re.DOTALL) # Pattern for generic ``` blocks - generic_blocks = re.findall(r"```\s*\n(.*?)\n```", content, re.DOTALL) + generic_blocks = re.findall(r"``\s*\n(.*?)\n``", content, re.DOTALL) all_blocks = [] @@ -146,17 +146,17 @@ class PythonExecutionReq(Requirement): and validates or executes it according to the configured execution mode. Args: - timeout (int): Maximum seconds to allow code to run. Defaults to ``5``. - allow_unsafe_execution (bool): If ``True``, execute code directly with + timeout (int): Maximum seconds to allow code to run. Defaults to `5`. + allow_unsafe_execution (bool): If `True`, execute code directly with subprocess. Use only with trusted sources. allowed_imports (list[str] | None): Allowlist of importable top-level - modules. ``None`` allows any import. - use_sandbox (bool): If ``True``, use ``llm-sandbox`` for Docker-based + modules. `None` allows any import. + use_sandbox (bool): If `True`, use `llm-sandbox` for Docker-based isolated execution. Attributes: validation_fn (Callable[[Context], ValidationResult]): The validation - function attached to this requirement; always non-``None``. + function attached to this requirement; always non-`None`. """ def __init__( diff --git a/mellea/stdlib/requirements/requirement.py b/mellea/stdlib/requirements/requirement.py index da848b6b8..801bf5478 100644 --- a/mellea/stdlib/requirements/requirement.py +++ b/mellea/stdlib/requirements/requirement.py @@ -12,7 +12,7 @@ class LLMaJRequirement(Requirement): """A requirement that always uses LLM-as-a-Judge. Any available constraint ALoRA will be ignored. Attributes: - use_aloras (bool): Always ``False`` for this class; ALoRA adapters are + use_aloras (bool): Always `False` for this class; ALoRA adapters are never used even if they are available. """ @@ -20,14 +20,14 @@ class LLMaJRequirement(Requirement): def requirement_check_to_bool(x: CBlock | str) -> bool: - """Checks if a given output should be marked converted to ``True``. + """Checks if a given output should be marked converted to `True`. - By default, the requirement check alora outputs: ``{"requirement_likelihood": 0.0}``. - Returns ``True`` if the likelihood value is > 0.5. + By default, the requirement check alora outputs: `{"requirement_likelihood": 0.0}`. + Returns `True` if the likelihood value is > 0.5. Args: x: ALoRA output string or CBlock containing JSON with a - ``requirement_likelihood`` field. + `requirement_likelihood` field. Returns: True if the extracted likelihood exceeds 0.5, False otherwise. @@ -58,16 +58,16 @@ def requirement_check_to_bool(x: CBlock | str) -> bool: class ALoraRequirement(Requirement, Intrinsic): """A requirement validated by an ALoRA adapter; falls back to LLM-as-a-Judge only on error. - If an exception is thrown during the ALoRA execution path, ``mellea`` will + If an exception is thrown during the ALoRA execution path, `mellea` will fall back to LLMaJ. That is the only case where LLMaJ will be used. Args: description (str): Human-readable requirement description. intrinsic_name (str | None): Name of the ALoRA intrinsic to use. - Defaults to ``"requirement-check"``. + Defaults to `"requirement-check"`. Attributes: - use_aloras (bool): Always ``True``; this class always attempts to use + use_aloras (bool): Always `True`; this class always attempts to use ALoRA adapters for validation. """ @@ -96,13 +96,13 @@ def reqify(r: str | Requirement) -> Requirement: This is a utility method for functions that allow you to pass in Requirements as either explicit Requirement objects or strings that you intend to be interpreted as requirements. Args: - r: A ``Requirement`` object or a plain string description to wrap as one. + r: A `Requirement` object or a plain string description to wrap as one. Returns: - A ``Requirement`` instance. + A `Requirement` instance. Raises: - Exception: If ``r`` is neither a ``str`` nor a ``Requirement`` instance. + Exception: If `r` is neither a `str` nor a `Requirement` instance. """ if type(r) is str: return Requirement(r) @@ -113,27 +113,27 @@ def reqify(r: str | Requirement) -> Requirement: def req(*args, **kwargs) -> Requirement: - """Shorthand for ``Requirement.__init__``. + """Shorthand for `Requirement.__init__`. Args: - *args: Positional arguments forwarded to ``Requirement.__init__``. - **kwargs: Keyword arguments forwarded to ``Requirement.__init__``. + *args: Positional arguments forwarded to `Requirement.__init__`. + **kwargs: Keyword arguments forwarded to `Requirement.__init__`. Returns: - A new ``Requirement`` instance. + A new `Requirement` instance. """ return Requirement(*args, **kwargs) def check(*args, **kwargs) -> Requirement: - """Shorthand for ``Requirement.__init__(..., check_only=True)``. + """Shorthand for `Requirement.__init__(..., check_only=True)`. Args: - *args: Positional arguments forwarded to ``Requirement.__init__``. - **kwargs: Keyword arguments forwarded to ``Requirement.__init__``. + *args: Positional arguments forwarded to `Requirement.__init__`. + **kwargs: Keyword arguments forwarded to `Requirement.__init__`. Returns: - A new ``Requirement`` instance with ``check_only=True``. + A new `Requirement` instance with `check_only=True`. """ return Requirement(*args, **kwargs, check_only=True) @@ -170,11 +170,11 @@ def simple_validate( reason: only used if the provided function returns a bool; if the validation function fails, a static reason for that failure to give to the llm when repairing Returns: - A validation function that takes a ``Context`` and returns a ``ValidationResult``. + A validation function that takes a `Context` and returns a `ValidationResult`. Raises: - ValueError: If ``fn`` returns a type other than ``bool`` or - ``tuple[bool, str]``. + ValueError: If `fn` returns a type other than `bool` or + `tuple[bool, str]`. """ def validate(ctx: Context) -> ValidationResult: diff --git a/mellea/stdlib/requirements/safety/guardian.py b/mellea/stdlib/requirements/safety/guardian.py index 4e99bc95a..6f0f3e70e 100644 --- a/mellea/stdlib/requirements/safety/guardian.py +++ b/mellea/stdlib/requirements/safety/guardian.py @@ -51,7 +51,7 @@ def get_available_risks(cls) -> list[str]: """Return a list of all available risk type identifiers. Returns: - list[str]: String values of all ``GuardianRisk`` enum members. + list[str]: String values of all `GuardianRisk` enum members. """ return [risk.value for risk in cls] @@ -90,19 +90,19 @@ class GuardianCheck(Requirement): Args: risk (str | GuardianRisk | None): The type of risk to check for. Required - unless ``custom_criteria`` is provided. - backend_type (BackendType): Backend type to use -- ``"ollama"`` or - ``"huggingface"``. + unless `custom_criteria` is provided. + backend_type (BackendType): Backend type to use -- `"ollama"` or + `"huggingface"`. model_version (str | None): Specific Guardian model version. Defaults to the appropriate 8B model for the chosen backend. device (str | None): Device string for HuggingFace inference (e.g. - ``"cuda"``). + `"cuda"`). ollama_url (str | None): Base URL for the Ollama server. When - ``None``, reads ``OLLAMA_HOST`` from the environment (falling back - to ``http://localhost:11434``). + `None`, reads `OLLAMA_HOST` from the environment (falling back + to `http://localhost:11434`). thinking (bool): Enable chain-of-thought reasoning mode in the Guardian model. custom_criteria (str | None): Free-text criteria string used in place of a - standard ``GuardianRisk`` value. + standard `GuardianRisk` value. context_text (str | None): Context document for groundedness checks. tools (list[dict] | None): Tool schemas for function-call validation. backend (Backend | None): Pre-initialised backend instance to reuse; avoids @@ -204,8 +204,8 @@ def __init__( def get_effective_risk(self) -> str: """Return the effective risk criteria to use for validation. - Returns the ``custom_criteria`` string when one was provided, otherwise - returns the ``risk`` identifier set during initialisation. + Returns the `custom_criteria` string when one was provided, otherwise + returns the `risk` identifier set during initialisation. Returns: str: The active risk/criteria string forwarded to the Guardian model. @@ -217,7 +217,7 @@ def get_available_risks(cls) -> list[str]: """Return a list of all available standard risk type identifiers. Returns: - list[str]: String values of all ``GuardianRisk`` enum members. + list[str]: String values of all `GuardianRisk` enum members. """ return GuardianRisk.get_available_risks() @@ -252,7 +252,7 @@ async def validate( """Validate a conversation using Granite Guardian via the selected backend. Builds a minimal chat context from the current session context, invokes the - Guardian model, and parses its ``yes/no`` output. A ``"No"`` + Guardian model, and parses its `yes/no` output. A `"No"` label (risk not detected) is treated as a passing validation result. Args: @@ -265,8 +265,8 @@ async def validate( Guardian backend call. Returns: - ValidationResult: ``result=True`` when the content is considered safe - (Guardian returns ``"No"``), ``result=False`` otherwise. + ValidationResult: `result=True` when the content is considered safe + (Guardian returns `"No"`), `result=False` otherwise. """ logger = self._logger diff --git a/mellea/stdlib/requirements/tool_reqs.py b/mellea/stdlib/requirements/tool_reqs.py index e4f4181da..9d5eb4f54 100644 --- a/mellea/stdlib/requirements/tool_reqs.py +++ b/mellea/stdlib/requirements/tool_reqs.py @@ -1,9 +1,9 @@ -"""``Requirement`` factories for tool-use validation. +"""`Requirement` factories for tool-use validation. -Provides ``uses_tool``, a ``Requirement`` factory that validates whether a model +Provides `uses_tool`, a `Requirement` factory that validates whether a model response includes a call to a specified tool — useful when you need to enforce tool invocation via rejection sampling rather than relying solely on the model's -``tool_choice`` setting. Also provides ``tool_arg_validator``, which validates the +`tool_choice` setting. Also provides `tool_arg_validator`, which validates the value of a specific argument to a named tool. Both accept either the tool's string name or its callable. """ @@ -30,10 +30,10 @@ def uses_tool(tool_name: str | Callable, check_only: bool = False) -> Requiremen tool_name: The tool that must be called; this can be either the name of the tool or the Callable for the tool. check_only: Propagates to the Requirement. - Use ``tool_choice`` if the OpenAI ``tool_choice`` model option is supported by your model and inference engine. + Use `tool_choice` if the OpenAI `tool_choice` model option is supported by your model and inference engine. Returns: - A ``Requirement`` that validates whether the specified tool was called. + A `Requirement` that validates whether the specified tool was called. """ tool_name = _name2str(tool_name) @@ -74,7 +74,7 @@ def tool_arg_validator( 2. should this be done automatically when the user provides asserts in their function body? Returns: - A ``Requirement`` that validates the specified tool argument. + A `Requirement` that validates the specified tool argument. """ if tool_name is not None: tool_name = _name2str(tool_name) diff --git a/mellea/stdlib/sampling/base.py b/mellea/stdlib/sampling/base.py index 67a116d00..f37d5822f 100644 --- a/mellea/stdlib/sampling/base.py +++ b/mellea/stdlib/sampling/base.py @@ -46,7 +46,7 @@ class BaseSamplingStrategy(SamplingStrategy): Args: loop_budget (int): Maximum number of generate/validate cycles. Must be - greater than 0. Defaults to ``1``. + greater than 0. Defaults to `1`. requirements (list[Requirement] | None): Global requirements evaluated on every sample. When set, overrides per-call requirements. diff --git a/mellea/stdlib/sampling/budget_forcing.py b/mellea/stdlib/sampling/budget_forcing.py index 5da1d0fae..548586dd3 100644 --- a/mellea/stdlib/sampling/budget_forcing.py +++ b/mellea/stdlib/sampling/budget_forcing.py @@ -26,25 +26,25 @@ class BudgetForcingSamplingStrategy(RejectionSamplingStrategy): """Sampling strategy that enforces a token budget for chain-of-thought reasoning. - Extends ``RejectionSamplingStrategy`` with explicit control over the ```` + Extends `RejectionSamplingStrategy` with explicit control over the `` block size and the answer block size. On each loop iteration, - ``think_budget_forcing`` interleaves forced-thinking and final-answer generation, + `think_budget_forcing` interleaves forced-thinking and final-answer generation, after which the standard rejection-sampling validation pass determines whether to accept or retry. - Currently only supports the ``OllamaModelBackend``. + Currently only supports the `OllamaModelBackend`. Args: think_max_tokens (int | None): Tokens allocated for the thinking block. - Defaults to ``4096``. + Defaults to `4096`. answer_max_tokens (int | None): Tokens allocated for the answer block. - ``None`` means unbounded. + `None` means unbounded. start_think_token (str | None): Token opening the thinking block. - Defaults to ``""``. + Defaults to `""`. end_think_token (str | None): Token closing the thinking block. - Defaults to ``""``. + Defaults to `""`. begin_response_token (str | None): Optional token opening the response - block. Defaults to ``""``. + block. Defaults to `""`. end_response_token (str): Token closing the response block. think_more_suffix (str | None): Suffix to force continued thinking. Empty string disables forcing. diff --git a/mellea/stdlib/sampling/majority_voting.py b/mellea/stdlib/sampling/majority_voting.py index 05b377c32..f7b7f0931 100644 --- a/mellea/stdlib/sampling/majority_voting.py +++ b/mellea/stdlib/sampling/majority_voting.py @@ -24,16 +24,16 @@ class BaseMBRDSampling(RejectionSamplingStrategy): Args: number_of_samples (int): Number of samples to generate and use for - majority voting. Defaults to ``8``. - weighted (bool): Not yet implemented. If ``True``, weights scores + majority voting. Defaults to `8`. + weighted (bool): Not yet implemented. If `True`, weights scores before majority vote. loop_budget (int): Inner rejection-sampling loop count. Must be > 0. requirements (list[Requirement] | None): Requirements to validate - against. If ``None``, uses per-call requirements. + against. If `None`, uses per-call requirements. Attributes: symmetric (bool): Whether the similarity metric is symmetric, allowing - the upper-triangle score matrix to be mirrored; always ``True`` for + the upper-triangle score matrix to be mirrored; always `True` for this base class. """ @@ -70,15 +70,15 @@ def compare_strings(self, ref: str, pred: str) -> float: pred (str): The predicted string to evaluate. Returns: - float: A similarity score, typically in ``[0.0, 1.0]`` where ``1.0`` + float: A similarity score, typically in `[0.0, 1.0]` where `1.0` indicates a perfect match. """ def maybe_apply_weighted(self, scr: np.ndarray) -> np.ndarray: - """Apply per-sample weights to the score vector if ``self.weighted`` is ``True``. + """Apply per-sample weights to the score vector if `self.weighted` is `True`. Currently not implemented; the input array is returned unchanged when - ``self.weighted`` is ``True``. + `self.weighted` is `True`. Args: scr (np.ndarray): 1-D array of aggregated similarity scores, one @@ -191,19 +191,19 @@ class MajorityVotingStrategyForMath(BaseMBRDSampling): """MajorityVoting Sampling Strategy for Math Expressions. Args: - number_of_samples (int): Number of samples to generate. Defaults to ``8``. - float_rounding (int): Decimal places for float comparison. Defaults to ``6``. - strict (bool): Enforce strict comparison mode. Defaults to ``True``. + number_of_samples (int): Number of samples to generate. Defaults to `8`. + float_rounding (int): Decimal places for float comparison. Defaults to `6`. + strict (bool): Enforce strict comparison mode. Defaults to `True`. allow_set_relation_comp (bool): Allow set-relation comparisons. Defaults - to ``False``. - weighted (bool): Not yet implemented. Defaults to ``False``. - loop_budget (int): Rejection-sampling loop count. Defaults to ``1``. + to `False`. + weighted (bool): Not yet implemented. Defaults to `False`. + loop_budget (int): Rejection-sampling loop count. Defaults to `1`. requirements (list[Requirement] | None): Requirements to validate against. Attributes: match_types (list[str]): Extraction target types used for parsing math - expressions; always ``["latex", "axpr"]``, computed at init. - symmetric (bool): Inherited from ``BaseMBRDSampling``; always ``True`` + expressions; always `["latex", "axpr"]`, computed at init. + symmetric (bool): Inherited from `BaseMBRDSampling`; always `True` for this strategy (set explicitly at init). """ @@ -253,16 +253,16 @@ def compare_strings(self, ref: str, pred: str) -> float: """Compare two strings using math-aware extraction and verification. Parses both strings into mathematical expressions using the configured - ``match_types`` (latex and/or expr), then verifies equivalence via - ``math_verify.verify``. + `match_types` (latex and/or expr), then verifies equivalence via + `math_verify.verify`. Args: ref (str): The reference (gold) string containing a math expression. pred (str): The predicted string to compare against the reference. Returns: - float: ``1.0`` if the expressions are considered equivalent, - ``0.0`` otherwise. + float: `1.0` if the expressions are considered equivalent, + `0.0` otherwise. """ # Convert string match_types to ExtractionTarget objects extraction_targets = [] @@ -291,16 +291,16 @@ class MBRDRougeLStrategy(BaseMBRDSampling): """Sampling Strategy that uses RougeL to compute symbol-level distances for majority voting. Args: - number_of_samples (int): Number of samples to generate. Defaults to ``8``. - weighted (bool): Not yet implemented. Defaults to ``False``. - loop_budget (int): Rejection-sampling loop count. Defaults to ``1``. + number_of_samples (int): Number of samples to generate. Defaults to `8`. + weighted (bool): Not yet implemented. Defaults to `False`. + loop_budget (int): Rejection-sampling loop count. Defaults to `1`. requirements (list[Requirement] | None): Requirements to validate against. Attributes: - match_types (list[str]): Rouge metric names used for scoring (``["rougeL"]``). - scorer (RougeScorer): Pre-configured ``RougeScorer`` instance used for + match_types (list[str]): Rouge metric names used for scoring (`["rougeL"]`). + scorer (RougeScorer): Pre-configured `RougeScorer` instance used for pairwise string comparison. - symmetric (bool): Inherited from ``BaseMBRDSampling``; always ``True`` for + symmetric (bool): Inherited from `BaseMBRDSampling`; always `True` for RougeL (the score is symmetric by construction). """ @@ -339,7 +339,7 @@ def compare_strings(self, ref: str, pred: str) -> float: pred (str): The predicted string to evaluate. Returns: - float: RougeL F-measure score in the range ``[0.0, 1.0]``. + float: RougeL F-measure score in the range `[0.0, 1.0]`. """ scr: float = self.scorer.score(ref, pred)[self.match_types[-1]].fmeasure return scr diff --git a/mellea/stdlib/sampling/sampling_algos/budget_forcing_alg.py b/mellea/stdlib/sampling/sampling_algos/budget_forcing_alg.py index 7d211e386..dc1fad2c8 100644 --- a/mellea/stdlib/sampling/sampling_algos/budget_forcing_alg.py +++ b/mellea/stdlib/sampling/sampling_algos/budget_forcing_alg.py @@ -1,11 +1,11 @@ """Budget-forcing generation algorithm for thinking models. -Implements ``think_budget_forcing``, which extends a model's reasoning phase by +Implements `think_budget_forcing`, which extends a model's reasoning phase by repeatedly appending a "think more" suffix whenever the model attempts to close its -```` block prematurely, following the method proposed in arXiv:2501.19393. -Generation is split into a thinking pass (bounded by ``think_max_tokens``) and an -answer pass (bounded by ``answer_max_tokens``), using the raw completions API of an -``OllamaModelBackend``. +`` block prematurely, following the method proposed in arXiv:2501.19393. +Generation is split into a thinking pass (bounded by `think_max_tokens`) and an +answer pass (bounded by `answer_max_tokens`), using the raw completions API of an +`OllamaModelBackend`. """ from typing import Any @@ -47,20 +47,20 @@ async def think_budget_forcing( Args: backend: OllamaModelBackend instance to use for generation. - action: The last item of the context, passed as an ``action`` instead of as part - of the ``ctx``. See ``docs/dev/generate_signature_decisions.md``. + action: The last item of the context, passed as an `action` instead of as part + of the `ctx`. See `docs/dev/generate_signature_decisions.md`. ctx: The current conversation context. format: Optional Pydantic model for constrained decoding of the response. - tool_calls: If ``True``, tool calling is enabled. + tool_calls: If `True`, tool calling is enabled. think_max_tokens: Budget in number of tokens allocated for the think block. answer_max_tokens: Budget in number of tokens allocated for the summary and - answer block; ``None`` indicates unbounded answer, generating till EoS. - start_think_token: String indicating start of think block, default ````. - end_think_token: String indicating end of think block, default ````. + answer block; `None` indicates unbounded answer, generating till EoS. + start_think_token: String indicating start of think block, default ``. + end_think_token: String indicating end of think block, default ``. begin_response_token: Used by certain models, string indicating start of - response block, e.g. ``""``, default ``""``. + response block, e.g. `""`, default `""`. think_more_suffix: String to append to force continued thinking, e.g. - ``"\nWait"``; if ``None``, additional thinking is not forced (upper-bound + `"\nWait"`; if `None`, additional thinking is not forced (upper-bound budget case). answer_suffix: String to append to force a final answer. model_options: Any model options to upsert into the defaults for this call. @@ -70,7 +70,7 @@ async def think_budget_forcing( Raises: Exception: If the backend returns generation results without the - required ``meta`` information (e.g. token usage counts). + required `meta` information (e.g. token usage counts). Assumptions: - The chat template is applied on prompt, with think mode enabled diff --git a/mellea/stdlib/sampling/sofai.py b/mellea/stdlib/sampling/sofai.py index 792de2de4..f24127b2e 100644 --- a/mellea/stdlib/sampling/sofai.py +++ b/mellea/stdlib/sampling/sofai.py @@ -41,7 +41,7 @@ class SOFAISamplingStrategy(SamplingStrategy): results. If S1 Solver fails after exhausting the budget or shows no improvement, escalates to a single attempt with S2 Solver (slow model). - The strategy leverages ``ValidationResult.reason`` fields to provide targeted + The strategy leverages `ValidationResult.reason` fields to provide targeted feedback for repair, enabling more effective iterative improvement. Args: @@ -52,9 +52,9 @@ class SOFAISamplingStrategy(SamplingStrategy): s2_solver_mode (Literal["fresh_start", "continue_chat", "best_attempt"]): How to invoke the S2 solver when S1 fails. loop_budget (int): Maximum number of S1 repair attempts before escalating - to S2. Must be greater than 0. Defaults to ``3``. + to S2. Must be greater than 0. Defaults to `3`. judge_backend (Backend | None): Optional backend for LLM-as-Judge - validation. If ``None``, falls back to the session backend. + validation. If `None`, falls back to the session backend. feedback_strategy (Literal["simple", "first_error", "all_errors"]): Detail level of repair feedback provided to the S1 solver. diff --git a/mellea/stdlib/session.py b/mellea/stdlib/session.py index ab2a965f3..651f8204f 100644 --- a/mellea/stdlib/session.py +++ b/mellea/stdlib/session.py @@ -1,10 +1,10 @@ -"""``MelleaSession``: the primary entry point for running generative programs. +"""`MelleaSession`: the primary entry point for running generative programs. -``MelleaSession`` wraps a ``Backend`` and a ``Context`` and exposes high-level methods -(``act``, ``instruct``, ``sample``) that drive the generate-validate-repair loop. It -also manages a global context variable (accessible via ``get_session()``) so that +`MelleaSession` wraps a `Backend` and a `Context` and exposes high-level methods +(`act`, `instruct`, `sample`) that drive the generate-validate-repair loop. It +also manages a global context variable (accessible via `get_session()`) so that nested components can reach the current session without explicit threading. Use -``start_session(...)`` as a context manager to create and automatically clean up a +`start_session(...)` as a context manager to create and automatically clean up a session. """ @@ -67,7 +67,7 @@ def get_session() -> MelleaSession: """Get the current session from context. Returns: - The currently active ``MelleaSession``. + The currently active `MelleaSession`. Raises: RuntimeError: If no session is currently active. @@ -108,15 +108,15 @@ def start_session( model_id: Model identifier or name. Can be a `ModelIdentifier` from mellea.backends.model_ids or a string model name. ctx: Context instance for conversation history. Defaults to - ``SimpleContext()``. Mutually exclusive with ``context_type``. - context_type: Shorthand for creating a context — ``"simple"`` for - ``SimpleContext``, ``"chat"`` for ``ChatContext``. Mutually - exclusive with ``ctx``. + `SimpleContext()`. Mutually exclusive with `context_type`. + context_type: Shorthand for creating a context — `"simple"` for + `SimpleContext`, `"chat"` for `ChatContext`. Mutually + exclusive with `ctx`. model_options: Additional model configuration options that will be passed to the backend (e.g., temperature, max_tokens, etc.). plugins: Optional list of plugins scoped to this session. Accepts - ``@hook``-decorated functions, ``@plugin``-decorated class instances, - ``MelleaPlugin`` instances, or ``PluginSet`` instances. + `@hook`-decorated functions, `@plugin`-decorated class instances, + `MelleaPlugin` instances, or `PluginSet` instances. **backend_kwargs: Additional keyword arguments passed to the backend constructor. Returns: @@ -124,8 +124,8 @@ def start_session( or called directly with session methods. Raises: - ValueError: If both ``ctx`` and ``context_type`` are provided. - Exception: If ``backend_name`` is not one of the recognised backend + ValueError: If both `ctx` and `context_type` are provided. + Exception: If `backend_name` is not one of the recognised backend identifiers. ImportError: If the requested backend requires optional dependencies that are not installed. @@ -249,11 +249,11 @@ class MelleaSession: backend (Backend): The backend to use for all model inference in this session. ctx (Context | None): The conversation context. Defaults to a new - ``SimpleContext`` if ``None``. + `SimpleContext` if `None`. Attributes: - ctx (Context): The active conversation context; never ``None`` (defaults - to a fresh ``SimpleContext`` when ``None`` is passed). Updated after + ctx (Context): The active conversation context; never `None` (defaults + to a fresh `SimpleContext` when `None` is passed). Updated after every call that produces model output. id (str): Unique session UUID assigned at construction. """ @@ -354,8 +354,8 @@ def clone(self) -> MelleaSession: def reset(self): """Reset the context state to a fresh, empty context of the same type. - Fires the ``SESSION_RESET`` plugin hook if any plugins are registered, then - replaces ``self.ctx`` with the result of ``ctx.reset_to_new()``, discarding + Fires the `SESSION_RESET` plugin hook if any plugins are registered, then + replaces `self.ctx` with the result of `ctx.reset_to_new()`, discarding all accumulated conversation history. """ if has_plugins(HookType.SESSION_RESET): @@ -530,8 +530,8 @@ def instruct( images: A list of images to be used in the instruction or None if none. Returns: - A ``ModelOutputThunk`` if ``return_sampling_results`` is ``False``, - else a ``SamplingResult``. + A `ModelOutputThunk` if `return_sampling_results` is `False`, + else a `SamplingResult`. """ r = mfuncs.instruct( description, @@ -576,17 +576,17 @@ def chat( Args: content: The message text to send. - role: The role for the outgoing message (default ``"user"``). + role: The role for the outgoing message (default `"user"`). images: Optional list of images to include in the message. documents: Optional documents to attach to the message. Each element - may be a string or a ``Document`` object. - user_variables: Optional Jinja variable substitutions applied to ``content``. + may be a string or a `Document` object. + user_variables: Optional Jinja variable substitutions applied to `content`. format: Optional Pydantic model for constrained decoding of the response. model_options: Additional model options to merge with backend defaults. tool_calls: If true, tool calling is enabled. Returns: - The assistant ``Message`` response. + The assistant `Message` response. """ result, context = mfuncs.chat( content=content, @@ -617,15 +617,15 @@ def validate( """Validates a set of requirements over the output (if provided) or the current context (if the output is not provided). Args: - reqs: A single ``Requirement`` or a list of them to validate. - output: Optional model output ``CBlock`` to validate against instead of the context. + reqs: A single `Requirement` or a list of them to validate. + output: Optional model output `CBlock` to validate against instead of the context. format: Optional Pydantic model for constrained decoding. model_options: Additional model options to merge with backend defaults. generate_logs: Optional list to append generation logs to. - input: Optional input ``CBlock`` to include alongside ``output`` when validating. + input: Optional input `CBlock` to include alongside `output` when validating. Returns: - List of ``ValidationResult`` objects, one per requirement. + List of `ValidationResult` objects, one per requirement. """ return mfuncs.validate( reqs=reqs, @@ -925,9 +925,9 @@ async def ainstruct( await_result: if False and strategy is None, returns uncomputed ModelOutputThunk for streaming. Default is False. Returns: - A ``ComputedModelOutputThunk`` if ``strategy`` is ``None`` and ``await_results`` is ``False``, - else returns a ``ModelOutputThunk`` if return_sampling_results`` is ``False``, - else a ``SamplingResult``. + A `ComputedModelOutputThunk` if `strategy` is `None` and `await_results` is `False`, + else returns a `ModelOutputThunk` if return_sampling_results` is `False``, + else a `SamplingResult`. """ r = await mfuncs.ainstruct( description, @@ -973,17 +973,17 @@ async def achat( Args: content: The message text to send. - role: The role for the outgoing message (default ``"user"``). + role: The role for the outgoing message (default `"user"`). images: Optional list of images to include in the message. documents: Optional documents to attach to the message. Each element - may be a string or a ``Document`` object. - user_variables: Optional Jinja variable substitutions applied to ``content``. + may be a string or a `Document` object. + user_variables: Optional Jinja variable substitutions applied to `content`. format: Optional Pydantic model for constrained decoding of the response. model_options: Additional model options to merge with backend defaults. tool_calls: If true, tool calling is enabled. Returns: - The assistant ``Message`` response. + The assistant `Message` response. """ result, context = await mfuncs.achat( content=content, @@ -1014,15 +1014,15 @@ async def avalidate( """Validates a set of requirements over the output (if provided) or the current context (if the output is not provided). Args: - reqs: A single ``Requirement`` or a list of them to validate. - output: Optional model output ``CBlock`` to validate against instead of the context. + reqs: A single `Requirement` or a list of them to validate. + output: Optional model output `CBlock` to validate against instead of the context. format: Optional Pydantic model for constrained decoding. model_options: Additional model options to merge with backend defaults. generate_logs: Optional list to append generation logs to. - input: Optional input ``CBlock`` to include alongside ``output`` when validating. + input: Optional input `CBlock` to include alongside `output` when validating. Returns: - List of ``ValidationResult`` objects, one per requirement. + List of `ValidationResult` objects, one per requirement. """ return await mfuncs.avalidate( reqs=reqs, @@ -1131,13 +1131,13 @@ async def atransform( def powerup(cls, powerup_cls: type): """Appends methods in a class object `powerup_cls` to MelleaSession. - Iterates over all functions defined on ``powerup_cls`` and attaches each - one as a method on the ``MelleaSession`` class, effectively extending + Iterates over all functions defined on `powerup_cls` and attaches each + one as a method on the `MelleaSession` class, effectively extending the session with domain-specific helpers at runtime. Args: powerup_cls (type): A class whose functions should be added to - ``MelleaSession`` as instance methods. + `MelleaSession` as instance methods. """ for name, fn in inspect.getmembers(powerup_cls, predicate=inspect.isfunction): setattr(cls, name, fn) diff --git a/mellea/stdlib/start_backend.py b/mellea/stdlib/start_backend.py index 897c7f897..15784784e 100644 --- a/mellea/stdlib/start_backend.py +++ b/mellea/stdlib/start_backend.py @@ -1,4 +1,4 @@ -"""Typed ``start_backend`` with overloaded return types.""" +"""Typed `start_backend` with overloaded return types.""" from __future__ import annotations @@ -24,16 +24,16 @@ def backend_name_to_class(name: str) -> Any: """Resolves backend names to Backend classes. Args: - name: Short backend name, e.g. ``"ollama"``, ``"hf"``, ``"openai"``, - ``"watsonx"``, or ``"litellm"``. + name: Short backend name, e.g. `"ollama"`, `"hf"`, `"openai"`, + `"watsonx"`, or `"litellm"`. Returns: - The corresponding ``Backend`` class, or ``None`` if the name is unrecognised. + The corresponding `Backend` class, or `None` if the name is unrecognised. Raises: ImportError: If the requested backend has optional dependencies that are - not installed (e.g. ``mellea[hf]``, ``mellea[watsonx]``, or - ``mellea[litellm]``). + not installed (e.g. `mellea[hf]`, `mellea[watsonx]`, or + `mellea[litellm]`). """ if name == "ollama": from ..backends.ollama import OllamaModelBackend @@ -80,10 +80,10 @@ def backend_name_to_class(name: str) -> Any: def _resolve_context( ctx: Context | None, context_type: Literal["simple", "chat"] | None ) -> Context: - """Resolve a ``Context`` from explicit instance and/or shorthand name. + """Resolve a `Context` from explicit instance and/or shorthand name. Raises: - ValueError: If both ``ctx`` and ``context_type`` are provided. + ValueError: If both `ctx` and `context_type` are provided. """ if ctx is not None and context_type is not None: raise ValueError("Cannot specify both 'ctx' and 'context_type'.") @@ -321,31 +321,31 @@ def start_backend( ) -> tuple[Context, Backend]: """Create a context and backend pair without a full session. - Accepts the same backend/model/context arguments as ``start_session`` but - returns the raw ``(Context, Backend)`` tuple for callers that manage their + Accepts the same backend/model/context arguments as `start_session` but + returns the raw `(Context, Backend)` tuple for callers that manage their own inference loop. Args: - backend_name: The backend to use (``"ollama"``, ``"hf"``, ``"openai"``, - ``"watsonx"``, or ``"litellm"``). + backend_name: The backend to use (`"ollama"`, `"hf"`, `"openai"`, + `"watsonx"`, or `"litellm"`). model_id: Model identifier or name. - ctx: An explicit ``Context`` instance. Mutually exclusive with - ``context_type``. - context_type: Shorthand for creating a context — ``"simple"`` for - ``SimpleContext``, ``"chat"`` for ``ChatContext``. Mutually - exclusive with ``ctx``. + ctx: An explicit `Context` instance. Mutually exclusive with + `context_type`. + context_type: Shorthand for creating a context — `"simple"` for + `SimpleContext`, `"chat"` for `ChatContext`. Mutually + exclusive with `ctx`. model_options: Additional model configuration options passed to the backend. **backend_kwargs: Additional keyword arguments passed to the backend constructor. Returns: - Tuple of ``(Context, Backend)`` with types narrowed by ``backend_name`` - and ``context_type``. + Tuple of `(Context, Backend)` with types narrowed by `backend_name` + and `context_type`. Raises: - ValueError: If both ``ctx`` and ``context_type`` are provided. - Exception: If ``backend_name`` is not recognised. + ValueError: If both `ctx` and `context_type` are provided. + Exception: If `backend_name` is not recognised. """ resolved_ctx = _resolve_context(ctx, context_type) backend_class = backend_name_to_class(backend_name) diff --git a/mellea/stdlib/streaming.py b/mellea/stdlib/streaming.py index c2804fe88..2de0c6d37 100644 --- a/mellea/stdlib/streaming.py +++ b/mellea/stdlib/streaming.py @@ -48,11 +48,11 @@ class StreamEvent: """Base class for all streaming events emitted by :func:`stream_with_chunking`. - The ``timestamp`` field is auto-populated at instantiation time; callers - do not set it. Because ``timestamp`` has ``init=False`` it is never part - of ``__init__``, so subclasses may declare additional fields in any order - without conflict. Any new ``init=False`` fields on subclasses must also - use ``field(..., init=False)``. + The `timestamp` field is auto-populated at instantiation time; callers + do not set it. Because `timestamp` has `init=False` it is never part + of `__init__`, so subclasses may declare additional fields in any order + without conflict. Any new `init=False` fields on subclasses must also + use `field(..., init=False)`. Attributes: timestamp: Unix timestamp (seconds) at the moment the event was created. @@ -65,14 +65,14 @@ class StreamEvent: class ChunkEvent(StreamEvent): """Emitted after each validated chunk is delivered to the consumer. - Fired after all active requirements' ``stream_validate`` calls return - non-``"fail"`` for this chunk and the chunk has been placed on the + Fired after all active requirements' `stream_validate` calls return + non-`"fail"` for this chunk and the chunk has been placed on the consumer queue. Args: text: The chunk text that was validated and emitted. chunk_index: Zero-based position of this chunk in the stream. - attempt: Sampling attempt number (always ``1`` in v1). + attempt: Sampling attempt number (always `1` in v1). """ text: str @@ -85,16 +85,16 @@ class QuickCheckEvent(StreamEvent): """Emitted after each per-chunk streaming validation batch. One event per chunk, covering all active requirements in parallel. - Not emitted when there are no ``requirements``. + Not emitted when there are no `requirements`. Args: chunk_index: Zero-based position of the chunk that was validated. - attempt: Sampling attempt number (always ``1`` in v1). - passed: ``True`` if all active requirements returned non-``"fail"`` + attempt: Sampling attempt number (always `1` in v1). + passed: `True` if all active requirements returned non-`"fail"` for this chunk. results: :class:`~mellea.core.requirement.PartialValidationResult` from each active requirement, in the same order as the active - slice of ``requirements``. + slice of `requirements`. """ chunk_index: int @@ -110,10 +110,10 @@ class StreamingDoneEvent(StreamEvent): Fired after the regular token stream and any trailing fragment released by :meth:`~mellea.stdlib.chunking.ChunkingStrategy.flush` have both been processed. Only emitted on natural completion — not on early exit (a - requirement returned ``"fail"``) or on exception. + requirement returned `"fail"`) or on exception. Args: - attempt: Sampling attempt number (always ``1`` in v1). + attempt: Sampling attempt number (always `1` in v1). full_text: Complete accumulated text at stream end. """ @@ -129,8 +129,8 @@ class FullValidationEvent(StreamEvent): and the stream completed naturally. Not emitted on early exit. Args: - attempt: Sampling attempt number (always ``1`` in v1). - passed: ``True`` if all final + attempt: Sampling attempt number (always `1` in v1). + passed: `True` if all final :class:`~mellea.core.requirement.ValidationResult` objects passed. results: :class:`~mellea.core.requirement.ValidationResult` from each non-failed requirement, in requirement order. @@ -145,7 +145,7 @@ class FullValidationEvent(StreamEvent): class RetryEvent(StreamEvent): """Reserved for future use. - Defined for API completeness — ``RetryEvent`` is not emitted by the + Defined for API completeness — `RetryEvent` is not emitted by the v1 orchestrator because v1 retry is caller-driven re-invocation of :func:`stream_with_chunking`. When orchestrator-side retry is added, this event will fire before each re-attempt. @@ -164,14 +164,14 @@ class CompletedEvent(StreamEvent): """Emitted when the orchestrator exits, including early-exit cases. Always the last event before :meth:`StreamChunkingResult.events` - terminates. ``success`` reflects :attr:`StreamChunkingResult.completed`. + terminates. `success` reflects :attr:`StreamChunkingResult.completed`. Args: - success: ``True`` if the stream completed normally (no ``"fail"`` - result and no unhandled exception); ``False`` otherwise. + success: `True` if the stream completed normally (no `"fail"` + result and no unhandled exception); `False` otherwise. full_text: Complete accumulated text. On early exit or exception, reflects whatever was accumulated before cancellation. - attempts_used: Number of orchestrator invocations (always ``1`` in v1). + attempts_used: Number of orchestrator invocations (always `1` in v1). """ success: bool @@ -185,9 +185,9 @@ class ErrorEvent(StreamEvent): Args: exception_type: Python class name of the exception - (e.g. ``"ValueError"``). + (e.g. `"ValueError"`). detail: String representation of the exception. If - ``cancel_generation()`` also raised during cleanup, the cleanup + `cancel_generation()` also raised during cleanup, the cleanup error is appended. """ @@ -218,19 +218,19 @@ class StreamChunkingResult: ctx: The generation context returned alongside the MOT. Attributes: - completed: ``False`` if the stream exited early because a requirement - returned ``"fail"`` during streaming; ``True`` otherwise. + completed: `False` if the stream exited early because a requirement + returned `"fail"` during streaming; `True` otherwise. full_text: The generated text available after streaming completes. On natural completion, the full accumulated text. On early exit - (a requirement returned ``"fail"``), only the validated and emitted + (a requirement returned `"fail"`), only the validated and emitted portion — i.e. what consumers received via :meth:`astream`. Available after :meth:`acomplete` returns. final_validations: :class:`~mellea.core.requirement.ValidationResult` objects from the final :meth:`~mellea.core.requirement.Requirement.validate` calls on all non-failed requirements. Available after :meth:`acomplete` returns. - streaming_failures: ``(Requirement, PartialValidationResult)`` pairs - for every requirement that returned ``"fail"`` during streaming. + streaming_failures: `(Requirement, PartialValidationResult)` pairs + for every requirement that returned `"fail"` during streaming. """ def __init__(self, mot: ModelOutputThunk, ctx: Context) -> None: @@ -273,12 +273,12 @@ async def astream(self) -> AsyncIterator[str]: Each yielded string is a chunk that has passed per-chunk streaming validation (or the stream had no requirements). Iteration ends when all chunks have been yielded, whether the stream completed normally or - was cancelled early on a ``"fail"`` result. + was cancelled early on a `"fail"` result. **Single-consumer.** Chunks are delivered via an :class:`asyncio.Queue` that this method drains; calling - ``astream()`` a second time on the same result blocks indefinitely - because the queue is empty and the terminating ``None`` sentinel + `astream()` a second time on the same result blocks indefinitely + because the queue is empty and the terminating `None` sentinel has already been consumed. If you need the chunks after iteration, capture them into a list during the first pass or use :attr:`full_text` after :meth:`acomplete`. @@ -325,19 +325,19 @@ async def events(self) -> AsyncIterator[StreamEvent]: 1. :class:`QuickCheckEvent` / :class:`ChunkEvent` pairs, one per chunk (validation fires first; the chunk is released to the consumer only after passing). Includes any trailing fragment released by the - chunking strategy's ``flush()`` method. + chunking strategy's `flush()` method. 2. :class:`StreamingDoneEvent` — all chunks (including flush) delivered. - 3. :class:`FullValidationEvent` — final ``validate()`` calls returned. + 3. :class:`FullValidationEvent` — final `validate()` calls returned. 4. :class:`CompletedEvent` — orchestrator is exiting. - On early exit: :class:`QuickCheckEvent` (``passed=False``) is the + On early exit: :class:`QuickCheckEvent` (`passed=False`) is the last validation event, followed by :class:`CompletedEvent`. No :class:`StreamingDoneEvent` or :class:`FullValidationEvent` is emitted. On exception: :class:`ErrorEvent` followed by :class:`CompletedEvent`. **Single-consumer.** Events are delivered via a queue that this method - drains; calling ``events()`` a second time raises :exc:`RuntimeError`. + drains; calling `events()` a second time raises :exc:`RuntimeError`. Yields: StreamEvent: A typed event from the orchestrator. @@ -346,7 +346,7 @@ async def events(self) -> AsyncIterator[StreamEvent]: RuntimeError: If called more than once on the same result. Note: - ``events()`` itself never raises from the event stream. If the + `events()` itself never raises from the event stream. If the orchestrator encounters an unhandled exception, an :class:`ErrorEvent` is emitted and iteration ends normally. Exceptions surface to the caller via :meth:`astream` (as a @@ -373,8 +373,8 @@ async def acomplete(self) -> None: Raises: Exception: Propagates the orchestrator exception if :meth:`astream` - has not yet consumed it (raise-once — only one of ``astream`` - or ``acomplete`` raises, whichever drains the failure marker + has not yet consumed it (raise-once — only one of `astream` + or `acomplete` raises, whichever drains the failure marker first). asyncio.CancelledError: If the orchestration task was externally cancelled (e.g. via :func:`asyncio.wait_for` timeout). @@ -390,10 +390,10 @@ async def acomplete(self) -> None: # Raise-once: a prior call already surfaced the exception. if self._exception_surfaced: return - # ``task.exception()`` raises CancelledError on a cancelled task + # `task.exception()` raises CancelledError on a cancelled task # (rather than returning it), so check cancelled status first. # This branch covers BaseException paths that bypass the - # ``except Exception`` handler in ``_orchestrate_streaming``. + # `except Exception` handler in `_orchestrate_streaming`. if self._orchestration_task.cancelled(): self._exception_surfaced = True raise asyncio.CancelledError() @@ -406,20 +406,20 @@ async def acomplete(self) -> None: def as_thunk(self) -> ModelOutputThunk[str]: """Wrap the output as a computed :class:`~mellea.core.base.ModelOutputThunk`. - Returns a new thunk with ``value`` set to :attr:`full_text` and + Returns a new thunk with `value` set to :attr:`full_text` and generation metadata copied from the original MOT. Safe to call on - early-exit results; ``value`` reflects the validated and emitted + early-exit results; `value` reflects the validated and emitted portion (same as :attr:`full_text` — see its docstring). Note: - On early exit, ``cancel_generation()`` forces the MOT into a + On early exit, `cancel_generation()` forces the MOT into a computed state without running the backend's - ``post_processing()``. ``value`` and ``streaming`` are - reliable. ``parsed_repr`` is set to the raw text (same as - ``value``) — consistent with normal completion for plain-text + `post_processing()`. `value` and `streaming` are + reliable. `parsed_repr` is set to the raw text (same as + `value`) — consistent with normal completion for plain-text outputs, but for typed outputs the backend-parsed representation - will not be available. Telemetry fields (``generation.usage``, - ``generation.ttfb_ms``, etc.) may be ``None`` or reflect the + will not be available. Telemetry fields (`generation.usage`, + `generation.ttfb_ms`, etc.) may be `None` or reflect the partial state at cancellation time; usage totals are not recoverable. @@ -471,8 +471,8 @@ async def _orchestrate_streaming( async def _process_chunk(c: str, ci: int) -> bool: """Validate *c*, emit events, push to consumer queue. - Returns ``True`` if a ``"fail"`` was recorded (caller should - trigger early exit), ``False`` if the chunk was validated and + Returns `True` if a `"fail"` was recorded (caller should + trigger early exit), `False` if the chunk was validated and emitted successfully. """ active = [ @@ -731,32 +731,32 @@ async def stream_with_chunking( chunk in parallel across all requirements. For each new complete chunk produced by the chunking strategy, - ``stream_validate`` is called once per active requirement (in parallel + `stream_validate` is called once per active requirement (in parallel via :func:`asyncio.gather`), receiving that single chunk. Multiple - chunks produced from one ``astream()`` iteration are validated - sequentially in order, so early exit on a ``"fail"`` result prevents + chunks produced from one `astream()` iteration are validated + sequentially in order, so early exit on a `"fail"` result prevents later chunks in the same batch from being validated or emitted to the consumer. - If any requirement returns ``"fail"``, the generation is cancelled + If any requirement returns `"fail"`, the generation is cancelled immediately (via :meth:`~mellea.core.base.ModelOutputThunk.cancel_generation`) and - :attr:`StreamChunkingResult.completed` is set to ``False``. The + :attr:`StreamChunkingResult.completed` is set to `False`. The failing chunk is not emitted to the consumer; use :attr:`StreamChunkingResult.streaming_failures` to inspect what failed. When the stream ends naturally, any trailing fragment withheld by the chunking strategy (see :meth:`~mellea.stdlib.chunking.ChunkingStrategy.flush`) - is released as a final chunk and run through ``stream_validate`` on the + is released as a final chunk and run through `stream_validate` on the same terms as the regular chunks. On early exit, the trailing fragment is discarded because the generation was cancelled mid-token. - After the stream ends naturally, ``validate()`` is called on every - requirement that did not return ``"fail"`` — both ``"pass"`` and - ``"unknown"`` trigger final validation. On early exit, no ``validate()`` + After the stream ends naturally, `validate()` is called on every + requirement that did not return `"fail"` — both `"pass"` and + `"unknown"` trigger final validation. On early exit, no `validate()` call is made; :attr:`StreamChunkingResult.final_validations` remains - empty. Requirements are cloned (``copy(req)``) before backend generation - begins, so the originals are never mutated and a raising ``__copy__`` + empty. Requirements are cloned (`copy(req)`) before backend generation + begins, so the originals are never mutated and a raising `__copy__` cannot leak an in-flight backend task. The orchestrator emits typed :class:`StreamEvent` objects throughout @@ -764,14 +764,14 @@ async def stream_with_chunking( parallel with or instead of :meth:`StreamChunkingResult.astream`. Requirements that need context beyond the current chunk should - accumulate it themselves across ``stream_validate`` calls (e.g. - ``self._seen = self._seen + chunk``). They must not read ``mot.astream()`` + accumulate it themselves across `stream_validate` calls (e.g. + `self._seen = self._seen + chunk`). They must not read `mot.astream()` directly — this orchestrator is the single consumer of the MOT stream. Note: Chunks are emitted to the consumer (via :meth:`StreamChunkingResult.astream`) only after every requirement's - ``stream_validate`` has returned for that chunk. A slow validator + `stream_validate` has returned for that chunk. A slow validator (for example, one that invokes an LLM) therefore adds latency to every chunk — the consumer sees a chunk at most as quickly as the slowest active validator. This trade is deliberate in v1: it @@ -783,7 +783,7 @@ async def stream_with_chunking( Note: v1 retry is simple re-invocation of this function. Plugin hooks - (``SAMPLING_LOOP_START``, ``SAMPLING_REPAIR``, etc.) do not fire + (`SAMPLING_LOOP_START`, `SAMPLING_REPAIR`, etc.) do not fire during streaming — use :meth:`StreamChunkingResult.events` for observability instead. @@ -792,13 +792,13 @@ async def stream_with_chunking( backend: The backend used for generation and final validation. ctx: The generation context. requirements: Sequence of requirements to validate against each chunk - during streaming. ``None`` disables streaming validation (chunks - are still produced; ``validate()`` is not called at stream end). + during streaming. `None` disables streaming validation (chunks + are still produced; `validate()` is not called at stream end). chunking: Chunking strategy — either a :class:`~mellea.stdlib.chunking.ChunkingStrategy` - instance or one of the string aliases ``"sentence"`` (default), - ``"word"``, or ``"paragraph"``. + instance or one of the string aliases `"sentence"` (default), + `"word"`, or `"paragraph"`. validation_backend: Optional alternate backend for both - ``stream_validate`` and final ``validate`` calls. When ``None``, + `stream_validate` and final `validate` calls. When `None`, *backend* is used for validation. Returns: @@ -809,16 +809,16 @@ async def stream_with_chunking( Raises: ValueError: If *chunking* is a string that does not match any known - alias (``"sentence"``, ``"word"``, ``"paragraph"``). + alias (`"sentence"`, `"word"`, `"paragraph"`). RuntimeError: If the backend returns an already-computed :class:`~mellea.core.base.ModelOutputThunk` instead of a streaming one. This indicates the backend is not honouring - ``ModelOption.STREAM``. + `ModelOption.STREAM`. Note: - Any exception raised by ``copy(req)`` on a ``requirements`` entry + Any exception raised by `copy(req)` on a `requirements` entry propagates to the caller; no backend generation is started in that - case. See :class:`~mellea.core.Requirement` for the ``__copy__`` + case. See :class:`~mellea.core.Requirement` for the `__copy__` override contract. """ if isinstance(chunking, str): diff --git a/mellea/stdlib/tools/interpreter.py b/mellea/stdlib/tools/interpreter.py index 4c537cc32..7b303d3b9 100644 --- a/mellea/stdlib/tools/interpreter.py +++ b/mellea/stdlib/tools/interpreter.py @@ -1,13 +1,13 @@ """Code interpreter tool and execution environments for agentic workflows. -Provides ``ExecutionResult`` (capturing stdout, stderr, success, and optional static -analysis output) and three concrete ``ExecutionEnvironment`` implementations: -``StaticAnalysisEnvironment`` (parse and import-check only, no execution), -``UnsafeEnvironment`` (subprocess execution in the current Python environment), and -``LLMSandboxEnvironment`` (Docker-isolated execution via ``llm-sandbox``). All -environments support an optional ``allowed_imports`` allowlist. The top-level -``code_interpreter`` and ``local_code_interpreter`` functions are ready to be wrapped -as ``MelleaTool`` instances for ReACT or other agentic loops. +Provides `ExecutionResult` (capturing stdout, stderr, success, and optional static +analysis output) and three concrete `ExecutionEnvironment` implementations: +`StaticAnalysisEnvironment` (parse and import-check only, no execution), +`UnsafeEnvironment` (subprocess execution in the current Python environment), and +`LLMSandboxEnvironment` (Docker-isolated execution via `llm-sandbox`). All +environments support an optional `allowed_imports` allowlist. The top-level +`code_interpreter` and `local_code_interpreter` functions are ready to be wrapped +as `MelleaTool` instances for ReACT or other agentic loops. """ import ast @@ -40,13 +40,13 @@ class ExecutionResult: TODO: should we also be trying to pass back the value of the final expression evaluated, or the value of locals() and globals()? Args: - success (bool): ``True`` if execution succeeded (exit code 0 or - static-analysis passed); ``False`` otherwise. - stdout (str | None): Captured standard output, or ``None`` if + success (bool): `True` if execution succeeded (exit code 0 or + static-analysis passed); `False` otherwise. + stdout (str | None): Captured standard output, or `None` if execution was skipped. - stderr (str | None): Captured standard error, or ``None`` if + stderr (str | None): Captured standard error, or `None` if execution was skipped. - skipped (bool): ``True`` when execution was not attempted. + skipped (bool): `True` when execution was not attempted. skip_message (str | None): Explanation of why execution was skipped. analysis_result (Any | None): Optional payload from static-analysis environments. @@ -97,7 +97,7 @@ class ExecutionEnvironment(ABC): Args: allowed_imports (list[str] | None): Allowlist of top-level module names - that generated code may import. ``None`` disables the import check. + that generated code may import. `None` disables the import check. """ @@ -131,8 +131,8 @@ def execute(self, code: str, timeout: int) -> ExecutionResult: compatibility. Returns: - ExecutionResult: Result with ``skipped=True`` and the parsed AST in - ``analysis_result`` on success, or a syntax-error description on + ExecutionResult: Result with `skipped=True` and the parsed AST in + `analysis_result` on success, or a syntax-error description on failure. """ try: @@ -245,9 +245,9 @@ class LLMSandboxEnvironment(ExecutionEnvironment): def execute(self, code: str, timeout: int) -> ExecutionResult: """Execute code using llm-sandbox in an isolated Docker container. - Checks the import allowlist first, then delegates to a ``SandboxSession`` - from the ``llm-sandbox`` package. Returns a skipped result if - ``llm-sandbox`` is not installed. + Checks the import allowlist first, then delegates to a `SandboxSession` + from the `llm-sandbox` package. Returns a skipped result if + `llm-sandbox` is not installed. Args: code (str): The Python source code to execute. @@ -341,7 +341,7 @@ def code_interpreter(code: str) -> ExecutionResult: code: The Python code to execute. Returns: - An ``ExecutionResult`` with stdout, stderr, and a success flag. + An `ExecutionResult` with stdout, stderr, and a success flag. """ exec_env = LLMSandboxEnvironment(allowed_imports=None) return exec_env.execute(code, 60) @@ -354,7 +354,7 @@ def local_code_interpreter(code: str) -> ExecutionResult: code: The Python code to execute. Returns: - An ``ExecutionResult`` with stdout, stderr, and a success flag. + An `ExecutionResult` with stdout, stderr, and a success flag. """ exec_env = UnsafeEnvironment(allowed_imports=None) return exec_env.execute(code, 60) diff --git a/mellea/stdlib/tools/mcp.py b/mellea/stdlib/tools/mcp.py index 698ffd9bb..25bd9196e 100644 --- a/mellea/stdlib/tools/mcp.py +++ b/mellea/stdlib/tools/mcp.py @@ -6,15 +6,15 @@ Two-step workflow: -1. ``discover_mcp_tools`` — inspect a server's tools without instantiating them. -2. ``MCPToolSpec.as_mellea_tool`` — build a callable ``MelleaTool`` from a spec. +1. `discover_mcp_tools` — inspect a server's tools without instantiating them. +2. `MCPToolSpec.as_mellea_tool` — build a callable `MelleaTool` from a spec. -Connection helpers (``http_connection``, ``sse_connection``, ``stdio_connection``) +Connection helpers (`http_connection`, `sse_connection`, `stdio_connection`) produce the config dicts fed into the functions above. Each tool invocation opens its own short-lived MCP session, so no session lifetime management is required by the caller. Async MCP calls are executed on -Mellea's shared background event loop via ``_run_async_in_thread``. +Mellea's shared background event loop via `_run_async_in_thread`. """ import asyncio @@ -51,7 +51,7 @@ class MCPToolSpec: """Metadata for a single tool from an MCP server. - Holds everything needed to inspect or instantiate a ``MelleaTool`` without + Holds everything needed to inspect or instantiate a `MelleaTool` without keeping a live session open. Args: @@ -59,8 +59,8 @@ class MCPToolSpec: description (str): Human-readable description from the server. input_schema (dict[str, Any]): OpenAI-compatible parameters schema dict. connection (dict[str, Any]): Transport config dict returned by one of the - connection helpers (``http_connection``, ``sse_connection``, - ``stdio_connection``). + connection helpers (`http_connection`, `sse_connection`, + `stdio_connection`). """ def __init__( @@ -77,16 +77,16 @@ def __init__( self._connection = connection def as_mellea_tool(self) -> MelleaTool: - """Create a callable ``MelleaTool`` from this spec. + """Create a callable `MelleaTool` from this spec. - The returned tool opens a fresh MCP session per call. For ``stdio`` + The returned tool opens a fresh MCP session per call. For `stdio` transport this means a new subprocess is spawned on every tool - invocation; prefer ``streamable_http`` or ``sse`` for + invocation; prefer `streamable_http` or `sse` for performance-sensitive use. Returns: - A ``MelleaTool`` instance ready to pass via ``ModelOption.TOOLS`` - or to an agent loop like ``react()``. + A `MelleaTool` instance ready to pass via `ModelOption.TOOLS` + or to an agent loop like `react()`. """ return MelleaTool( self.name, @@ -109,17 +109,17 @@ def __repr__(self) -> str: async def discover_mcp_tools(connection: dict[str, Any]) -> list[MCPToolSpec]: """Discover all tools on an MCP server and return their metadata. - Opens a single session, calls ``list_tools()``, then closes. No - ``MelleaTool`` objects are instantiated — callers can inspect and filter - the returned specs before calling ``MCPToolSpec.as_mellea_tool``. + Opens a single session, calls `list_tools()`, then closes. No + `MelleaTool` objects are instantiated — callers can inspect and filter + the returned specs before calling `MCPToolSpec.as_mellea_tool`. Args: connection: Transport config dict. Build it with one of the connection - helpers rather than constructing it by hand: ``http_connection``, - ``sse_connection``, ``stdio_connection``. + helpers rather than constructing it by hand: `http_connection`, + `sse_connection`, `stdio_connection`. Returns: - List of ``MCPToolSpec`` objects, one per tool on the server. + List of `MCPToolSpec` objects, one per tool on the server. """ async with _open_session(connection) as session: result = await session.list_tools() @@ -146,13 +146,13 @@ def http_connection( Args: url: MCP server URL. - api_key: Sets ``Authorization: Bearer ``. - headers: Additional headers, merged after ``api_key``. + api_key: Sets `Authorization: Bearer `. + headers: Additional headers, merged after `api_key`. connect_timeout: Seconds to wait for TCP connection (default 30). read_timeout: Seconds to wait for a response (default 300). Returns: - Connection dict ready to pass to ``discover_mcp_tools``. + Connection dict ready to pass to `discover_mcp_tools`. """ h: dict[str, str] = {} if api_key: @@ -180,13 +180,13 @@ def sse_connection( Args: url: MCP server URL. - api_key: Sets ``Authorization: Bearer ``. - headers: Additional headers, merged after ``api_key``. + api_key: Sets `Authorization: Bearer `. + headers: Additional headers, merged after `api_key`. connect_timeout: Seconds to wait for TCP connection (default 30). read_timeout: Seconds to wait for a response (default 300). Returns: - Connection dict ready to pass to ``discover_mcp_tools``. + Connection dict ready to pass to `discover_mcp_tools`. """ h: dict[str, str] = {} if api_key: @@ -212,13 +212,13 @@ def stdio_connection( """Build a stdio connection config. Args: - command: Executable to run (e.g. ``"gh"``). - args: Command-line arguments (e.g. ``["mcp", "serve"]``). + command: Executable to run (e.g. `"gh"`). + args: Command-line arguments (e.g. `["mcp", "serve"]`). env: Environment variables for the subprocess. timeout: Total seconds allowed for a tool call to complete (default 300). Returns: - Connection dict ready to pass to ``discover_mcp_tools``. + Connection dict ready to pass to `discover_mcp_tools`. """ conn: dict[str, Any] = { "transport": "stdio", @@ -335,8 +335,8 @@ def _make_sync_call(connection: dict[str, Any], tool_name: str) -> Callable[..., """Build a sync wrapper around an async MCP tool invocation. Runs the async call on Mellea's shared background event loop via - ``_run_async_in_thread``. MCP servers expect absent fields rather than - explicit ``null`` values, so ``None`` kwargs are stripped before the call. + `_run_async_in_thread`. MCP servers expect absent fields rather than + explicit `null` values, so `None` kwargs are stripped before the call. """ def sync_call(**kwargs: Any) -> str: diff --git a/mellea/telemetry/backend_instrumentation.py b/mellea/telemetry/backend_instrumentation.py index dc2d26fff..785ae9efc 100644 --- a/mellea/telemetry/backend_instrumentation.py +++ b/mellea/telemetry/backend_instrumentation.py @@ -30,8 +30,8 @@ def get_model_id_str(backend: Any) -> str: def get_system_name(backend: Any) -> str: """Get the Gen-AI system name from backend. - Kept for back-compatibility with existing dashboards keyed on ``gen_ai.system``. - New code should prefer ``get_provider_name()``. + Kept for back-compatibility with existing dashboards keyed on `gen_ai.system`. + New code should prefer `get_provider_name()`. Args: backend: Backend instance @@ -57,8 +57,8 @@ def get_system_name(backend: Any) -> str: def get_provider_name(backend: Any) -> str: """Get the Gen-AI provider name from backend. - Returns the value for ``gen_ai.provider.name`` (semconv v1.37.0+), which - supersedes the deprecated ``gen_ai.system`` attribute. + Returns the value for `gen_ai.provider.name` (semconv v1.37.0+), which + supersedes the deprecated `gen_ai.system` attribute. Args: backend: Backend instance @@ -269,14 +269,14 @@ def record_response_metadata( def finalize_backend_span(span: Any, *, error: Exception | None = None) -> None: """Close a backend span on the error path, setting error.type and ERROR status. - Used by the streaming error path in ``ModelOutputThunk.__aiter__`` where a + Used by the streaming error path in `ModelOutputThunk.__aiter__` where a span may be left open after an exception. Backends close spans on the - success path themselves via ``record_token_usage`` + ``record_response_metadata`` - + ``end_backend_span``. + success path themselves via `record_token_usage` + `record_response_metadata` + + `end_backend_span`. Args: - span: The span to finalise (no-op when ``None``). - error: Exception to record; sets ERROR status and ``error.type``. + span: The span to finalise (no-op when `None`). + error: Exception to record; sets ERROR status and `error.type`. """ if span is None: return diff --git a/mellea/telemetry/context.py b/mellea/telemetry/context.py index 8846df793..6a7d86126 100644 --- a/mellea/telemetry/context.py +++ b/mellea/telemetry/context.py @@ -1,6 +1,6 @@ """Async-safe context propagation for Mellea telemetry. -Carries ``session_id``, ``request_id``, ``model_id``, and ``sampling_iteration`` +Carries `session_id`, `request_id`, `model_id`, and `sampling_iteration` through async call chains via :mod:`contextvars`. Values automatically appear in log records (via :class:`MelleaContextFilter`) and can be attached to OpenTelemetry spans. @@ -58,47 +58,47 @@ def get_session_id() -> str | None: - """Return the session_id for the current async context, or ``None``. + """Return the session_id for the current async context, or `None`. Returns: - The active session ID string, or ``None`` if not set. + The active session ID string, or `None` if not set. """ return _session_id.get() def get_request_id() -> str | None: - """Return the request_id for the current async context, or ``None``. + """Return the request_id for the current async context, or `None`. Returns: - The active request ID string, or ``None`` if not set. + The active request ID string, or `None` if not set. """ return _request_id.get() def get_model_id() -> str | None: - """Return the model_id for the current async context, or ``None``. + """Return the model_id for the current async context, or `None`. Returns: - The active model ID string, or ``None`` if not set. + The active model ID string, or `None` if not set. """ return _model_id.get() def get_sampling_iteration() -> int | None: - """Return the sampling_iteration for the current async context, or ``None``. + """Return the sampling_iteration for the current async context, or `None`. Returns: - The current sampling iteration integer, or ``None`` if not set. + The current sampling iteration integer, or `None` if not set. """ return _sampling_iteration.get() def get_current_context() -> dict[str, Any]: - """Return a snapshot of all non-``None`` context values. + """Return a snapshot of all non-`None` context values. Returns: Mapping of field names to their current values, omitting keys whose - value is ``None``. + value is `None`. """ return {k: var.get() for k, var in _CONTEXT_VARS.items() if var.get() is not None} @@ -107,7 +107,7 @@ def generate_request_id() -> str: """Generate a new unique request ID (UUID4 hex string). Returns: - A short hex string suitable for use as a ``request_id``. + A short hex string suitable for use as a `request_id`. """ return uuid.uuid4().hex @@ -149,11 +149,11 @@ def with_context(**kwargs: Any) -> Generator[None, None, None]: """Synchronous context manager that sets telemetry context for the block duration. On exit the previous values are restored, making this safe for nested - usage and concurrent asyncio tasks (each ``asyncio.Task`` owns an isolated - copy of its ``ContextVar`` state). + usage and concurrent asyncio tasks (each `asyncio.Task` owns an isolated + copy of its `ContextVar` state). - Accepted keyword arguments: ``session_id``, ``request_id``, ``model_id``, - ``sampling_iteration``. Unknown keys raise :exc:`ValueError`. + Accepted keyword arguments: `session_id`, `request_id`, `model_id`, + `sampling_iteration`. Unknown keys raise :exc:`ValueError`. Args: **kwargs: Context fields to set within the block. @@ -180,9 +180,9 @@ def with_context(**kwargs: Any) -> Generator[None, None, None]: async def async_with_context(**kwargs: Any) -> AsyncGenerator[None, None]: """Async-with variant of :func:`with_context`. - Identical semantics but usable with ``async with`` syntax. Note that - :func:`with_context` also works inside ``async`` functions — use this - only when you specifically need ``async with`` syntax. + Identical semantics but usable with `async with` syntax. Note that + :func:`with_context` also works inside `async` functions — use this + only when you specifically need `async with` syntax. Args: **kwargs: Context fields to set within the block. @@ -208,8 +208,8 @@ async def async_with_context(**kwargs: Any) -> AsyncGenerator[None, None]: class MelleaContextFilter(logging.Filter): """Logging filter that injects telemetry context fields into every log record. - Fields from :func:`get_current_context` (``session_id``, ``request_id``, - ``model_id``, ``sampling_iteration``) are copied onto each + Fields from :func:`get_current_context` (`session_id`, `request_id`, + `model_id`, `sampling_iteration`) are copied onto each :class:`logging.LogRecord` so they appear automatically in structured JSON output produced by :class:`~mellea.core.utils.JsonFormatter`. """ @@ -221,7 +221,7 @@ def filter(self, record: logging.LogRecord) -> bool: record: The log record being processed. Returns: - Always ``True`` — the record is never suppressed. + Always `True` — the record is never suppressed. """ for key, value in get_current_context().items(): if not hasattr(record, key): diff --git a/mellea/telemetry/metrics.py b/mellea/telemetry/metrics.py index 3d1f9eabd..9a6a65b4e 100644 --- a/mellea/telemetry/metrics.py +++ b/mellea/telemetry/metrics.py @@ -617,7 +617,7 @@ def classify_error(exc: BaseException) -> str: exc: The exception to classify. Returns: - One of the ``ERROR_TYPE_*`` constants. + One of the `ERROR_TYPE_*` constants. """ # OpenAI SDK exceptions (optional dependency) try: @@ -698,7 +698,7 @@ def record_error( This is a no-op when metrics are disabled, ensuring zero overhead. Args: - error_type: Semantic error category (use ``ERROR_TYPE_*`` constants). + error_type: Semantic error category (use `ERROR_TYPE_*` constants). model: Model identifier (e.g. "gpt-4", "llama2:7b"). provider: Provider name (e.g. "openai", "ollama"). exception_class: Python exception class name (e.g. "RateLimitError"). @@ -755,13 +755,13 @@ def record_cost(cost: float, model: str, provider: str) -> None: """Record estimated LLM request cost in USD. This is a no-op when metrics are disabled, ensuring zero overhead. - Only call this when pricing data is available (i.e., ``compute_cost`` returned + Only call this when pricing data is available (i.e., `compute_cost` returned a non-None value). Args: cost: Estimated request cost in US dollars. - model: Model identifier (e.g. ``"gpt-4o"``, ``"claude-sonnet-4-6"``). - provider: Provider name (e.g. ``"openai"``, ``"ollama"``). + model: Model identifier (e.g. `"gpt-4o"`, `"claude-sonnet-4-6"`). + provider: Provider name (e.g. `"openai"`, `"ollama"`). Example: record_cost( @@ -827,7 +827,7 @@ def record_sampling_attempt(strategy: str) -> None: This is a no-op when metrics are disabled, ensuring zero overhead. Args: - strategy: Sampling strategy class name (e.g. ``"RejectionSamplingStrategy"``). + strategy: Sampling strategy class name (e.g. `"RejectionSamplingStrategy"`). """ if not _METRICS_ENABLED: return @@ -841,8 +841,8 @@ def record_sampling_outcome(strategy: str, success: bool) -> None: This is a no-op when metrics are disabled, ensuring zero overhead. Args: - strategy: Sampling strategy class name (e.g. ``"RejectionSamplingStrategy"``). - success: ``True`` if at least one attempt passed all requirements. + strategy: Sampling strategy class name (e.g. `"RejectionSamplingStrategy"`). + success: `True` if at least one attempt passed all requirements. """ if not _METRICS_ENABLED: return @@ -889,7 +889,7 @@ def record_requirement_check(requirement: str) -> None: This is a no-op when metrics are disabled, ensuring zero overhead. Args: - requirement: Requirement class name (e.g. ``"LLMaJRequirement"``). + requirement: Requirement class name (e.g. `"LLMaJRequirement"`). """ if not _METRICS_ENABLED: return @@ -903,8 +903,8 @@ def record_requirement_failure(requirement: str, reason: str) -> None: This is a no-op when metrics are disabled, ensuring zero overhead. Args: - requirement: Requirement class name (e.g. ``"LLMaJRequirement"``). - reason: Human-readable failure reason from ``ValidationResult.reason``. + requirement: Requirement class name (e.g. `"LLMaJRequirement"`). + reason: Human-readable failure reason from `ValidationResult.reason`. """ if not _METRICS_ENABLED: return @@ -937,7 +937,7 @@ def record_tool_call(tool: str, status: str) -> None: Args: tool: Name of the tool that was invoked. - status: ``"success"`` if the tool executed without error, ``"failure"`` otherwise. + status: `"success"` if the tool executed without error, `"failure"` otherwise. """ if not _METRICS_ENABLED: return diff --git a/mellea/telemetry/metrics_plugins.py b/mellea/telemetry/metrics_plugins.py index aea000d87..523588076 100644 --- a/mellea/telemetry/metrics_plugins.py +++ b/mellea/telemetry/metrics_plugins.py @@ -111,7 +111,7 @@ class ErrorMetricsPlugin(Plugin, name="error_metrics", priority=52): """Records LLM error counts from generation errors. This plugin hooks into the generation_error event to classify exceptions - by semantic error type and increment the ``mellea.llm.errors`` counter. + by semantic error type and increment the `mellea.llm.errors` counter. """ @hook("generation_error", mode=PluginMode.FIRE_AND_FORGET) @@ -188,8 +188,8 @@ async def record_cost_metrics( class SamplingMetricsPlugin(Plugin, name="sampling_metrics", priority=54): """Records sampling loop attempt and outcome metrics. - Hooks into ``sampling_iteration`` to count attempts per strategy and - ``sampling_loop_end`` to count successes and failures. + Hooks into `sampling_iteration` to count attempts per strategy and + `sampling_loop_end` to count successes and failures. """ @hook("sampling_iteration", mode=PluginMode.FIRE_AND_FORGET) @@ -224,7 +224,7 @@ async def record_sampling_outcome( class RequirementMetricsPlugin(Plugin, name="requirement_metrics", priority=55): """Records requirement validation check and failure metrics. - Hooks into ``validation_post_check`` to count checks and failures per + Hooks into `validation_post_check` to count checks and failures per requirement type after each validation batch. """ @@ -258,7 +258,7 @@ async def record_requirement_metrics( class ToolMetricsPlugin(Plugin, name="tool_metrics", priority=56): """Records tool invocation metrics. - Hooks into ``tool_post_invoke`` to count tool calls by name and success/failure status. + Hooks into `tool_post_invoke` to count tool calls by name and success/failure status. """ @hook("tool_post_invoke", mode=PluginMode.FIRE_AND_FORGET) diff --git a/mellea/telemetry/pricing.py b/mellea/telemetry/pricing.py index a33be56c3..c0a24d248 100644 --- a/mellea/telemetry/pricing.py +++ b/mellea/telemetry/pricing.py @@ -1,20 +1,20 @@ """LLM pricing via litellm's pricing API. -Pricing metrics require the litellm package (``mellea[litellm]``). Pricing is +Pricing metrics require the litellm package (`mellea[litellm]`). Pricing is auto-enabled when litellm is installed and can be explicitly controlled via the -``MELLEA_PRICING_ENABLED`` environment variable. +`MELLEA_PRICING_ENABLED` environment variable. -``MELLEA_PRICING_ENABLED`` tri-state: - - ``"true"`` + litellm installed → enabled - - ``"true"`` + litellm absent → warning, disabled - - ``"false"`` (any) → disabled (silent) +`MELLEA_PRICING_ENABLED` tri-state: + - `"true"` + litellm installed → enabled + - `"true"` + litellm absent → warning, disabled + - `"false"` (any) → disabled (silent) - unset + litellm installed → enabled (auto) - unset + litellm absent → disabled (silent) -Pricing is only active when ``MELLEA_METRICS_ENABLED`` is also set. +Pricing is only active when `MELLEA_METRICS_ENABLED` is also set. Custom pricing: - Set ``MELLEA_PRICING_FILE`` to a JSON file using litellm's native per-token + Set `MELLEA_PRICING_FILE` to a JSON file using litellm's native per-token schema. Minimal entries with only cost fields are supported:: { @@ -24,8 +24,8 @@ } } - Optional cache fields: ``cache_read_input_token_cost``, - ``cache_creation_input_token_cost``. + Optional cache fields: `cache_read_input_token_cost`, + `cache_creation_input_token_cost`. Environment variables: - MELLEA_PRICING_ENABLED: Tri-state pricing flag (true/false/unset). @@ -108,17 +108,17 @@ def compute_cost( """Estimate request cost in USD using litellm's pricing data. Args: - model: Model identifier (e.g. ``"gpt-5.4"``, ``"claude-sonnet-4-6"``). - provider: Provider name from the backend (e.g. ``"openai"``, ``"watsonx"``). - Passed to litellm as ``custom_llm_provider`` to aid model resolution — - e.g. ``"watsonx"`` causes litellm to try ``watsonx/ibm/granite-4-h-small``. - prompt_tokens: Total prompt tokens including any cached tokens, or ``None``. - completion_tokens: Number of completion tokens, or ``None``. - cached_tokens: Tokens served from prompt cache, or ``None``. - cache_creation_tokens: Tokens written to prompt cache, or ``None``. + model: Model identifier (e.g. `"gpt-5.4"`, `"claude-sonnet-4-6"`). + provider: Provider name from the backend (e.g. `"openai"`, `"watsonx"`). + Passed to litellm as `custom_llm_provider` to aid model resolution — + e.g. `"watsonx"` causes litellm to try `watsonx/ibm/granite-4-h-small`. + prompt_tokens: Total prompt tokens including any cached tokens, or `None`. + completion_tokens: Number of completion tokens, or `None`. + cached_tokens: Tokens served from prompt cache, or `None`. + cache_creation_tokens: Tokens written to prompt cache, or `None`. Returns: - Estimated cost in USD, or ``None`` if pricing is disabled or no pricing + Estimated cost in USD, or `None` if pricing is disabled or no pricing data exists for the model. """ if not _PRICING_ENABLED: diff --git a/mellea/telemetry/tracing.py b/mellea/telemetry/tracing.py index fc459f3ef..b427a57dc 100644 --- a/mellea/telemetry/tracing.py +++ b/mellea/telemetry/tracing.py @@ -106,7 +106,7 @@ def is_application_tracing_enabled() -> bool: Returns: True if application tracing has been enabled via the - ``MELLEA_TRACE_APPLICATION`` environment variable. + `MELLEA_TRACE_APPLICATION` environment variable. """ return _TRACE_APPLICATION_ENABLED @@ -116,7 +116,7 @@ def is_backend_tracing_enabled() -> bool: Returns: True if backend tracing has been enabled via the - ``MELLEA_TRACE_BACKEND`` environment variable. + `MELLEA_TRACE_BACKEND` environment variable. """ return _TRACE_BACKEND_ENABLED @@ -128,8 +128,8 @@ def is_content_tracing_enabled() -> bool: Enable only in controlled environments. Returns: - True if enabled via ``MELLEA_TRACE_CONTENT`` or - ``OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT``. + True if enabled via `MELLEA_TRACE_CONTENT` or + `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`. """ return _TRACE_CONTENT_ENABLED @@ -157,7 +157,7 @@ def trace_application(name: str, **attributes: Any) -> Generator[Any, None, None **attributes: Additional attributes to add to the span. Yields: - The span object if tracing is enabled, otherwise ``None``. + The span object if tracing is enabled, otherwise `None`. """ if _TRACE_APPLICATION_ENABLED and _application_tracer is not None: with _application_tracer.start_as_current_span(name) as span: # type: ignore @@ -180,7 +180,7 @@ def trace_backend(name: str, **attributes: Any) -> Generator[Any, None, None]: **attributes: Additional attributes to add to the span. Yields: - The span object if tracing is enabled, otherwise ``None``. + The span object if tracing is enabled, otherwise `None`. """ if _TRACE_BACKEND_ENABLED and _backend_tracer is not None: with _backend_tracer.start_as_current_span(name) as span: # type: ignore @@ -285,7 +285,7 @@ def set_span_status_error(span: Any, description: str) -> None: Use this for validation failures and other non-exception error conditions where the span should be marked failed but no exception was actually raised. - Calling ``set_span_error`` in these cases would create a misleading recorded + Calling `set_span_error` in these cases would create a misleading recorded exception event in OTEL traces. Args: From 0fb488de3059a508e11a3159c152825264bb12c5 Mon Sep 17 00:00:00 2001 From: Nigel Jones Date: Tue, 26 May 2026 12:57:55 +0100 Subject: [PATCH 2/2] docs(api): fix malformed docstring in session.py Pre-existing RST typo: ``return_sampling_results`` was missing its opening double-backtick, leaving a dangling backtick after the bulk normalisation. Correct the full inline-code span. Assisted-by: Claude Code Signed-off-by: Nigel Jones --- mellea/stdlib/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mellea/stdlib/session.py b/mellea/stdlib/session.py index 651f8204f..4563a7549 100644 --- a/mellea/stdlib/session.py +++ b/mellea/stdlib/session.py @@ -926,7 +926,7 @@ async def ainstruct( Returns: A `ComputedModelOutputThunk` if `strategy` is `None` and `await_results` is `False`, - else returns a `ModelOutputThunk` if return_sampling_results` is `False``, + else returns a `ModelOutputThunk` if `return_sampling_results` is `False`, else a `SamplingResult`. """ r = await mfuncs.ainstruct(