Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ After each memory step, Metis selects informative hidden states and updates the
```bash
conda env create -f environment.yml
conda activate metis
python -m pip install --no-build-isolation causal-conv1d==1.6.0 flash-attn==2.7.3
```

### 2. Run inference
Expand Down
1 change: 1 addition & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Metis 仍是一个早期研究系统,并非对外部记忆机制的完整替
```bash
conda env create -f environment.yml
conda activate metis
python -m pip install --no-build-isolation causal-conv1d==1.6.0 flash-attn==2.7.3
```

仓库中的环境文件是研究环境锁定文件,基于 Python 3.10、PyTorch 2.4.1 + CUDA 11.8、Transformers 5.4.0、DeepSpeed 和 Flash Linear Attention。CUDA 扩展可能需要根据具体硬件与平台进行调整。
Expand Down
23 changes: 10 additions & 13 deletions environment.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
name: metis
channels:
- conda-forge
- nvidia
- conda-forge
dependencies:
- _openmp_mutex=4.5
- bzip2=1.0.8
- ca-certificates=2026.6.17
- cuda-cccl=12.9.27
- cuda-cccl_linux-64=12.9.27
- cuda-cudart=11.8.89
- cuda-cudart-dev=11.8.89
- cuda-nvcc=11.8.89
- cuda-version=12.9
- nvidia::cuda-cccl=11.8.89
- nvidia::cuda-cudart=11.8.89
- nvidia::cuda-cudart-dev=11.8.89
- nvidia::cuda-nvcc=11.8.89
- ld_impl_linux-64=2.45.1
- libexpat=2.8.1
- libffi=3.5.2
Expand All @@ -36,6 +34,7 @@ dependencies:
- zstd=1.5.7
- pip:
- --extra-index-url=https://download.pytorch.org/whl/cu118
# CUDA extensions must be built after torch is installed; see the README.
- accelerate==1.14.0
- aiohappyeyeballs==2.6.2
- aiohttp==3.14.1
Expand All @@ -45,7 +44,6 @@ dependencies:
- anyio==4.14.0
- async-timeout==5.0.1
- attrs==26.1.0
- causal-conv1d==1.6.2.post1
- certifi==2026.6.17
- charset-normalizer==3.4.7
- click==8.4.1
Expand All @@ -57,7 +55,6 @@ dependencies:
- einops==0.8.2
- exceptiongroup==1.3.1
- filelock==3.29.0
- flash-attn==2.6.3
- flash-linear-attention==0.2.2
- fonttools==4.63.0
- frozenlist==1.8.0
Expand Down Expand Up @@ -96,7 +93,7 @@ dependencies:
- nvidia-cusolver-cu11==11.4.1.48
- nvidia-cusparse-cu11==11.7.5.86
- nvidia-ml-py==13.595.45
- nvidia-nccl-cu11==2.20.5
- nvidia-nccl-cu11==2.21.5
- nvidia-nvtx-cu11==11.8.86
- nvitop==1.7.0
- pandas==2.3.3
Expand Down Expand Up @@ -124,12 +121,12 @@ dependencies:
- six==1.17.0
- smmap==5.0.3
- socksio==1.0.0
- sympy==1.14.0
- sympy==1.13.1
- tokenizers==0.22.2
- torch==2.4.1+cu118
- torch==2.5.1+cu118
- tqdm==4.68.3
- transformers==5.4.0
- triton==3.0.0
- triton==3.1.0
- typer==0.25.1
- typing-extensions==4.15.0
- typing-inspection==0.4.2
Expand Down
2 changes: 1 addition & 1 deletion eval/configs/assets.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schema_version": 1,
"note": "Public model IDs may be replaced by local mirrors. Relative paths resolve from the Metis_dev repository root.",
"note": "Public model IDs may be replaced by local mirrors. Relative paths resolve from the repository root.",
"assets": {
"qwen3_5_4b": "Qwen/Qwen3.5-4B",
"qwen3_5_9b": "Qwen/Qwen3.5-9B",
Expand Down
8 changes: 5 additions & 3 deletions eval/experiments/ablation/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import shutil
import subprocess
import sys
import tempfile
from collections import defaultdict
from pathlib import Path
from typing import Any, Iterable
Expand All @@ -21,8 +22,9 @@


REPO_ROOT = Path(__file__).resolve().parents[3]
METIS_DEV_ROOT = Path(os.environ.get("METIS_MODEL_REPO_ROOT", str(REPO_ROOT)))
MODEL_REPO_ROOT = Path(os.environ.get("METIS_MODEL_REPO_ROOT", str(REPO_ROOT)))
DEFAULT_MATRIX = Path(__file__).resolve().parent / "configs" / "ablation_matrix.json"
DEFAULT_JUDGE_LOCK = Path(tempfile.gettempdir()) / "metis_eval_judge32.lock"
READY_STATUSES = {"ready", "reference_ready"}
REQUIRED_CHECKPOINT_FILES = {
"config.json",
Expand Down Expand Up @@ -562,7 +564,7 @@ def run_raw_stage(args: argparse.Namespace, matrix: dict[str, Any]) -> dict[str,
"matrix": str(args.matrix),
"matrix_sha256": sha256(args.matrix),
"git": git_snapshot(),
"metis_dev_git": git_snapshot(METIS_DEV_ROOT),
"model_repo_git": git_snapshot(MODEL_REPO_ROOT),
"python": args.python,
"checkpoints": selected,
"benchmarks": benchmarks,
Expand Down Expand Up @@ -915,7 +917,7 @@ def build_parser() -> argparse.ArgumentParser:
parser.add_argument("--judge-concurrency", type=int, default=0)
parser.add_argument("--judge-base-url", default="https://api.openai.com")
parser.add_argument("--api-key-env", default="OPENAI_API_KEY")
parser.add_argument("--judge-lock", type=Path, default=Path("/tmp/metis_eval_judge32.lock"))
parser.add_argument("--judge-lock", type=Path, default=DEFAULT_JUDGE_LOCK)
parser.add_argument("--judge-max-attempts", type=int, default=18)
parser.add_argument("--judge-retry-sleep", type=float, default=2.0)
parser.add_argument("--judge-retry-backoff", type=float, default=1.5)
Expand Down
35 changes: 29 additions & 6 deletions eval/tests/test_protocols.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import json
import re
import subprocess
import sys
import tempfile
Expand Down Expand Up @@ -160,15 +161,31 @@ def test_ablation_checkpoints_use_the_shared_asset_registry() -> None:
assert {item["id"] for item in matrix["checkpoints"]} <= set(assets)


def test_release_text_has_no_private_server_paths_or_usernames() -> None:
def test_release_text_has_no_private_paths_or_development_repo_names() -> None:
root = Path(__file__).resolve().parents[2]
forbidden = (
"/" + "mnt/afs/",
forbidden_literals = (
"/" + "mnt/",
"/" + "home/",
"/" + "root/",
"/" + "Users/",
"/" + "tmp/",
"api-" + "int.",
)
suffixes = {".py", ".json", ".md", ".toml", ".yml", ".txt"}
forbidden_repo_names = (
"Metis_" + "dev",
"Metis_" + "open",
)
suffixes = {
".cff",
".json",
".md",
".py",
".sh",
".toml",
".txt",
".yaml",
".yml",
}
candidates = subprocess.run(
[
"git",
Expand All @@ -177,7 +194,6 @@ def test_release_text_has_no_private_server_paths_or_usernames() -> None:
"--cached",
"--others",
"--exclude-standard",
"eval",
],
cwd=root,
check=True,
Expand All @@ -189,7 +205,14 @@ def test_release_text_has_no_private_server_paths_or_usernames() -> None:
path = root / relative.decode()
if path.is_file() and path.suffix in suffixes:
text = path.read_text(encoding="utf-8", errors="replace").lower()
assert not any(value.lower() in text for value in forbidden), path
assert not any(value.lower() in text for value in forbidden_literals), path
assert not any(
re.search(
rf"(?<![a-z0-9]){re.escape(value.lower())}(?![a-z0-9])",
text,
)
for value in forbidden_repo_names
), path


def test_generated_result_metadata_is_git_ignored() -> None:
Expand Down
29 changes: 26 additions & 3 deletions metis/backbone_wrappers/Qwen3_5_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@
from transformers.modeling_outputs import BaseModelOutputWithPast


def _zero3_safe_checkpoint(function, hidden_states):
"""Checkpoint frozen-backbone computation safely under ZeRO-3.

Reentrant checkpoint avoids the PyTorch 2.5 non-reentrant saved-tensor
metadata conflict with ZeRO-3 parameter re-partitioning.

If the input does not require gradients, the enclosed backbone operation
is fully frozen, so checkpointing it provides no backward-memory benefit.
"""
if not torch.is_grad_enabled() or not hidden_states.requires_grad:
return function(hidden_states)

return _ckpt(
function,
hidden_states,
use_reentrant=True,
preserve_rng_state=True,
)


class Qwen3_5DecoderLayerForMetis(DecoderLayerWrapperForMetis):
def __init__(self, config, raw_decoder):
super().__init__(config, raw_decoder)
Expand Down Expand Up @@ -137,7 +157,10 @@ def _attn(hidden_states):
attn_output = self_attn.o_proj(attn_output)
return memory_for_query, attn_output, residual

memory_for_query, attn_output, residual = _ckpt(_attn, hidden_states, use_reentrant=False)
memory_for_query, attn_output, residual = _zero3_safe_checkpoint(
_attn,
hidden_states,
)

return memory_for_query, attn_output, {'residual': residual}, self_attn.o_proj

Expand Down Expand Up @@ -183,7 +206,7 @@ def _mix(hidden_states):
)
return residual + mixed

hidden_states = _ckpt(_mix, hidden_states, use_reentrant=False)
hidden_states = _zero3_safe_checkpoint(_mix, hidden_states)

elif self.raw_decoder.layer_type == "full_attention":
residual = cache_dict['residual']
Expand All @@ -194,7 +217,7 @@ def _mlp(hidden_states):
normed = self.raw_decoder.post_attention_layernorm(hidden_states)
return residual + self.raw_decoder.mlp(normed)

hidden_states = _ckpt(_mlp, hidden_states, use_reentrant=False)
hidden_states = _zero3_safe_checkpoint(_mlp, hidden_states)

return hidden_states

Expand Down
Loading