Metadata-Version: 2.2
Name: ymark
Version: 0.1.1
Summary: LLM watermarking engine with vLLM and SGLang integration
Keywords: watermark,llm,vllm,sglang,ai-act,kirchenbauer
License: Apache-2.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: C++
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Project-URL: Homepage, https://github.com/yoonhyunwoo/ymark
Project-URL: Repository, https://github.com/yoonhyunwoo/ymark
Project-URL: Documentation, https://github.com/yoonhyunwoo/ymark/blob/main/docs/integration-guide.md
Requires-Python: >=3.9
Requires-Dist: torch
Provides-Extra: vllm
Requires-Dist: vllm; extra == "vllm"
Provides-Extra: sglang
Requires-Dist: sglang; extra == "sglang"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Description-Content-Type: text/markdown

# ymark

LLM watermarking engine with vLLM and SGLang integration.

Implements the Kirchenbauer et al. (2023) green/red-list watermark: at each generation step, the vocabulary is split into a green list and a red list based on the preceding token. Green-list logits receive an additive bias (`delta`), causing the sampler to favour green tokens ~80% of the time. Detection is a one-proportion z-test on the observed green ratio.

## Install

```bash
pip install ymark
```

### From source

```bash
git clone https://github.com/yoonhyunwoo/ymark.git
cd ymark
pip install .
```

Requires CMake >= 3.18 and a C++17 compiler.

## Quick start

### Embed watermark during generation

**vLLM:**

```python
from vllm import LLM, SamplingParams
from ymark import WatermarkConfig
from ymark.integrations.vllm import VLLMWatermarkProcessor

config = WatermarkConfig(delta=2.0, gamma=0.5, seed=42)
processor = VLLMWatermarkProcessor(config)

llm = LLM(model="Qwen/Qwen3-8B")
output = llm.generate(
    ["Hello"],
    SamplingParams(max_tokens=200, logits_processors=[processor]),
)
```

**SGLang:**

```python
from ymark import WatermarkConfig
from ymark.integrations.sglang import SGLangWatermarkProcessor

config = WatermarkConfig(delta=2.0, gamma=0.5, seed=42)
processor = SGLangWatermarkProcessor(config)
```

### Detect watermark in text

```python
from transformers import AutoTokenizer
from ymark import WatermarkConfig, WatermarkDetector

config = WatermarkConfig(seed=42)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B")

detector = WatermarkDetector(config, tokenizer)
result = detector.detect("some suspect text ...")
print(result)
# WATERMARKED | z=8.50 green_ratio=82.0% tokens=180 (threshold z>4.0)
```

## Configuration

| Parameter | Default | Description |
|-----------|---------|-------------|
| `delta`   | 2.0     | Bias added to green-list logits. Higher = easier detection, more quality impact. |
| `gamma`   | 0.5     | Fraction of vocab designated as green (0-1). 0.5 = even split. |
| `seed`    | 0       | Master seed. Different providers use different seeds to distinguish their watermarks. |

## How it works

```
Input tokens → Model → logits (vocab_size)
                              ↓
                    WatermarkProcessor
                    ├── seed * 1_000_003 + prev_token → RNG
                    ├── torch.rand(vocab_size) < gamma → green mask
                    └── logits[green] += delta
                              ↓
                    softmax → sample → next token
```

Normal text: green tokens selected ~50% of the time.
Watermarked text: green tokens selected ~80% of the time.

Detection counts the green ratio and applies a z-test. z > 4.0 → 99.99% confidence the text is watermarked.

## Architecture

```
ymark/
├── cpp/                        # C++ core (mt19937_64 RNG, mask generation)
│   ├── watermark_processor.h
│   └── watermark_processor.cc

        # pybind11
├── src/ymark/          # Python package
│   ├── binding.cc           # pybind11 bindings
│   ├── config.py               # WatermarkConfig
│   ├── processor.py            # WatermarkProcessor (wraps C++)
│   ├── detector.py             # WatermarkDetector (z-test)
│   └── integrations/
│       ├── vllm.py             # VLLMWatermarkProcessor
│       └── sglang.py           # SGLangWatermarkProcessor
├── tests/
│   └── test_watermark.py       # 14 tests
├── CMakeLists.txt
└── pyproject.toml              # scikit-build-core
```

The C++ core ensures deterministic green/red partitioning across all platforms. Both the processor (embedding) and detector use the same C++ RNG, guaranteeing mask consistency.

## Performance

Green mask generation: `std::mt19937_64` + `uniform_real_distribution` in C++.
Masks are cached per `(prev_token, vocab_size)` pair.

128K vocab benchmark: sub-millisecond per token.

## Verified

Tested on NVIDIA RTX 3060 with Qwen2.5-0.5B:

| Condition | z-score | green ratio | Verdict |
|-----------|---------|-------------|---------|
| Watermarked (delta=2.0) | 7.44 | 76.4% | WATERMARKED |
| Not watermarked | 0.78 | 52.8% | not watermarked |

## Reference

Kirchenbauer, J., et al. (2023). *A Watermark for Large Language Models*. arXiv:2301.10226

## License

Apache-2.0
