[watermarking] golden tests for backwards compatibility (#56809)

Signed-off-by: Raphael Rialland <raphael.rialland@mistral.ai> Signed-off-by: Simon Veitner <sveitner@redhat.com> Co-authored-by: Simon Veitner <sveitner@redhat.com> Co-authored-by: OpenAI Codex <codex@openai.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>

Raphaël Rialland committed Sep 26, 2026 at 00:01 UTC 4ccfe1239843998f9b3e109f159278dc0fcbf753
7 files changed +3866 -3
.buildkite/test_areas/misc.yaml
+2
@@ -211,6 +211,7 @@ steps:
211 - vllm/utils/
212 - vllm/v1/
213 - tests/v1
214 + - tests/watermarking
215 device: cpu-small
216 commands:
217 # split the test to avoid interference
@@ -222,6 +223,7 @@ steps:
223 - pytest -v -s -m 'cpu_test' v1/kv_connector/unit
224 - pytest -v -s -m 'cpu_test' v1/ec_connector/unit
225 - pytest -v -s -m 'cpu_test' v1/metrics
226 + - pytest -v -s watermarking
227
228 - label: ":nvidia: (H200 MIG 18GB) Extract Hidden States Integration"
229 key: extract-hidden-states-integration
tests/watermarking/__init__.py new
+2
@@ -0,0 +1,2 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
tests/watermarking/generate_goldens.py new
+24
@@ -0,0 +1,24 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +
4 +"""Regenerate tests/watermarking/watermarking_goldens.json."""
5 +
6 +import json
7 +from pathlib import Path
8 +
9 +from tests.watermarking.golden_candidates import golden_payload, validate_golden_guards
10 +
11 +GOLDENS_PATH = Path(__file__).with_name("watermarking_goldens.json")
12 +
13 +
14 +def main() -> None:
15 + payload = golden_payload()
16 + validate_golden_guards(payload["candidates"])
17 + GOLDENS_PATH.write_text(
18 + json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8"
19 + )
20 + print(f"wrote {len(payload['candidates'])} candidates to {GOLDENS_PATH}")
21 +
22 +
23 +if __name__ == "__main__":
24 + main()
tests/watermarking/golden_candidates.py new
+885
@@ -0,0 +1,885 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +
4 +"""Deterministic watermarking golden candidates."""
5 +
6 +import json
7 +import math
8 +from collections.abc import Mapping
9 +from dataclasses import dataclass, replace
10 +from pathlib import Path
11 +from typing import get_args
12 +
13 +import torch
14 +
15 +from vllm.config.watermarking import (
16 + WatermarkConfig,
17 + WatermarkContextScope,
18 + WatermarkingAlgorithm,
19 + WatermarkPRFName,
20 + derive_watermark_key,
21 +)
22 +from vllm.v1.watermarking import (
23 + DualKeyGumbelWatermarkDetector,
24 + GumbelWatermarkDetector,
25 + WatermarkDetection,
26 + WatermarkDetector,
27 + create_prf,
28 + create_watermarker,
29 +)
30 +from vllm.v1.worker.gpu.sample.watermark import repeated_context_mask
31 +
32 +GOLDEN_SCHEMA_VERSION = 3
33 +
34 +# JSON floats round-trip exactly, but libm differs by an ULP across builds.
35 +GOLDEN_FLOAT_RTOL = 1e-9
36 +GOLDEN_FLOAT_ATOL = 1e-300
37 +
38 +P_VALUE_RATIO_MIN_DISTANCE = 1e-6
39 +REPETITIVE_MIN_KEY_PERTURBATION_TOKENS = 8
40 +REPETITIVE_MAX_P_VALUE_RATIO = 0.5
41 +ROUTING_BOUNDARY_MIN_DISTANCE = 1e-6
42 +
43 +WATERMARK_CONFIG_FIELDS = (
44 + "algorithm",
45 + "alpha",
46 + "context_width",
47 + "deduplicate_contexts",
48 + "deduplicate_contexts_max_history",
49 + "prf",
50 + "allow_target_only_watermarking",
51 +)
52 +
53 +# round(((i + 1) * (5**0.5 - 1) / 2) % 1.0, 4) for i in range(64)
54 +ROUTING_UNIFORMS: tuple[float, ...] = (
55 + 0.618, 0.2361, 0.8541, 0.4721, 0.0902, 0.7082, 0.3262, 0.9443,
56 + 0.5623, 0.1803, 0.7984, 0.4164, 0.0344, 0.6525, 0.2705, 0.8885,
57 + 0.5066, 0.1246, 0.7426, 0.3607, 0.9787, 0.5967, 0.2148, 0.8328,
58 + 0.4508, 0.0689, 0.6869, 0.305, 0.923, 0.541, 0.1591, 0.7771,
59 + 0.3951, 0.0132, 0.6312, 0.2492, 0.8673, 0.4853, 0.1033, 0.7214,
60 + 0.3394, 0.9574, 0.5755, 0.1935, 0.8115, 0.4296, 0.0476, 0.6656,
61 + 0.2837, 0.9017, 0.5197, 0.1378, 0.7558, 0.3738, 0.9919, 0.6099,
62 + 0.2279, 0.846, 0.464, 0.082, 0.7001, 0.3181, 0.9361, 0.5542,
63 +) # fmt: skip
64 +
65 +
66 +class GoldenFormatError(ValueError):
67 + """The stored goldens do not have the shape this module expects."""
68 +
69 +
70 +class GoldenGuardError(ValueError):
71 + """A candidate is too weak or too unstable to be frozen as a golden."""
72 +
73 +
74 +@dataclass(frozen=True)
75 +class DeterministicGenerationFixture:
76 + vocabulary_size: int = 128
77 + num_tokens: int = 64
78 + logit_denominator: int = 16
79 + logit_modulus: int = 131
80 + dominant_token: int | None = None
81 + dominant_bias: int = 0
82 + prompt: tuple[int, ...] = ()
83 + routing_uniforms: tuple[float, ...] = ROUTING_UNIFORMS
84 +
85 + def logits(self, position: int) -> torch.Tensor:
86 + token_ids = torch.arange(self.vocabulary_size, dtype=torch.int64)
87 + values = (token_ids * 37 + position * 17) % self.logit_modulus - 56
88 + if self.dominant_token is not None:
89 + values[self.dominant_token] += self.dominant_bias
90 + return (values.to(torch.float32) / self.logit_denominator).unsqueeze(0)
91 +
92 +
93 +@dataclass(frozen=True)
94 +class WatermarkingSchemeConfig:
95 + context_width: int = 4
96 + generation_alpha: float = 0.1
97 + detection_alpha: float = 0.1
98 + generation_deduplicate_contexts: WatermarkContextScope = "single_turn"
99 + generation_deduplicate_contexts_max_history: int | None = 8192
100 + detection_deduplicate_contexts: bool = True
101 + p_value_threshold: float = 0.01
102 +
103 +
104 +@dataclass(frozen=True)
105 +class GenerationTrace:
106 + routing_draws: int
107 + key_b_routed: int | None
108 + key_b_effective: int | None
109 + dedup_skips: int
110 + partial_context_skips: int
111 +
112 +
113 +class _DeterministicSampler:
114 + def __init__(self, routing_uniforms: tuple[float, ...]) -> None:
115 + self.routing_uniforms = routing_uniforms
116 + self.routing_position = 0
117 + self.key_b_routed = 0
118 +
119 + def __call__(self, logits: torch.Tensor) -> torch.Tensor:
120 + if logits.shape[-1] != 2:
121 + return logits.argmax(dim=-1)
122 +
123 + probability_a = logits.softmax(dim=-1)[:, 0]
124 + uniform = self.routing_uniforms[
125 + self.routing_position % len(self.routing_uniforms)
126 + ]
127 + self.routing_position += 1
128 + routed = (probability_a <= uniform).to(torch.int64)
129 + self.key_b_routed += int(routed.sum())
130 + return routed
131 +
132 +
133 +def routing_boundary(alpha: float) -> float:
134 + """Return the realised key-A probability the deterministic sampler sees."""
135 + logits = torch.tensor([1 - alpha, alpha], dtype=torch.float32).log()
136 + return float(logits.softmax(dim=-1)[0])
137 +
138 +
139 +def _create_gumbel_detector(
140 + key: int,
141 + config: WatermarkingSchemeConfig,
142 + prf: WatermarkPRFName,
143 +) -> WatermarkDetector:
144 + return GumbelWatermarkDetector(
145 + key=key,
146 + context_width=config.context_width,
147 + p_value_threshold=config.p_value_threshold,
148 + prf=prf,
149 + deduplicate_contexts=config.detection_deduplicate_contexts,
150 + )
151 +
152 +
153 +def _create_dual_key_gumbel_detector(
154 + key: int,
155 + config: WatermarkingSchemeConfig,
156 + prf: WatermarkPRFName,
157 +) -> WatermarkDetector:
158 + return DualKeyGumbelWatermarkDetector(
159 + key=key,
160 + context_width=config.context_width,
161 + p_value_threshold=config.p_value_threshold,
162 + prf=prf,
163 + deduplicate_contexts=config.detection_deduplicate_contexts,
164 + alpha=config.detection_alpha,
165 + )
166 +
167 +
168 +DETECTOR_FACTORIES = {
169 + "gumbel": _create_gumbel_detector,
170 + "dual_key_gumbel": _create_dual_key_gumbel_detector,
171 +}
172 +
173 +
174 +@dataclass(frozen=True)
175 +class WatermarkingCandidate:
176 + id: str
177 + scheme: WatermarkingAlgorithm
178 + scheme_config: WatermarkingSchemeConfig
179 + prf: WatermarkPRFName
180 + key: int
181 + detection_key: int
182 + fixture: DeterministicGenerationFixture = DeterministicGenerationFixture()
183 +
184 + def configuration(self) -> dict[str, object]:
185 + prf_version = getattr(create_prf(self.prf, self.key), "version", None)
186 + if not isinstance(prf_version, str):
187 + raise ValueError(f"PRF {self.prf} does not define a version")
188 + return {
189 + "scheme": self.scheme,
190 + "scheme_config": {
191 + "context_width": self.scheme_config.context_width,
192 + "generation_alpha": self.scheme_config.generation_alpha,
193 + "detection_alpha": self.scheme_config.detection_alpha,
194 + "generation_deduplicate_contexts": (
195 + self.scheme_config.generation_deduplicate_contexts
196 + ),
197 + "generation_deduplicate_contexts_max_history": (
198 + self.scheme_config.generation_deduplicate_contexts_max_history
199 + ),
200 + "detection_deduplicate_contexts": (
201 + self.scheme_config.detection_deduplicate_contexts
202 + ),
203 + "p_value_threshold": self.scheme_config.p_value_threshold,
204 + },
205 + "prf": self.prf,
206 + "prf_version": prf_version,
207 + "key": str(self.key),
208 + "detection_key": str(self.detection_key),
209 + "fixture": {
210 + "vocabulary_size": self.fixture.vocabulary_size,
211 + "num_tokens": self.fixture.num_tokens,
212 + "logit_denominator": self.fixture.logit_denominator,
213 + "logit_modulus": self.fixture.logit_modulus,
214 + "dominant_token": self.fixture.dominant_token,
215 + "dominant_bias": self.fixture.dominant_bias,
216 + "prompt": list(self.fixture.prompt),
217 + },
218 + }
219 +
220 + def resolved(self) -> dict[str, object]:
221 + """Return the vLLM-side state this candidate resolves to.
222 +
223 + An allowlist rather than the whole dataclass, so a new production field
224 + is a deliberate golden change: WATERMARK_CONFIG_FIELDS names the fields
225 + recorded here and test_goldens.py checks it still covers
226 + WatermarkConfig. Keys exceed 2**53 and are decimal strings.
227 + """
228 + config = self._watermark_config()
229 + detector = self._detector()
230 + key_b_prf = getattr(detector, "key_b_prf", None)
231 + dual_key = self.scheme == "dual_key_gumbel"
232 + return {
233 + "watermark_config": {
234 + field: getattr(config, field) for field in WATERMARK_CONFIG_FIELDS
235 + },
236 + "derived_keys": {
237 + "key_a": str(derive_watermark_key(self.key, b"key_a"))
238 + if dual_key
239 + else None,
240 + "key_b": str(derive_watermark_key(self.key, b"key_b"))
241 + if dual_key
242 + else None,
243 + },
244 + "detector": {
245 + "type": type(detector).__name__,
246 + "context_width": detector.context_width,
247 + "p_value_threshold": detector.p_value_threshold,
248 + "deduplicate_contexts": detector.deduplicate_contexts,
249 + "alpha": getattr(detector, "alpha", None),
250 + "prf_key": str(detector.prf.key),
251 + "key_b_prf_key": None if key_b_prf is None else str(key_b_prf.key),
252 + },
253 + }
254 +
255 + def generate(self) -> list[int]:
256 + return self.generate_with_trace()[0]
257 +
258 + def generate_with_trace(self) -> tuple[list[int], GenerationTrace]:
259 + watermarker = create_watermarker(self._watermark_config())
260 + sampler = _DeterministicSampler(self.fixture.routing_uniforms)
261 + generated: list[int] = []
262 + dedup_skips = 0
263 + partial_context_skips = 0
264 + key_b_effective = 0
265 +
266 + for position in range(self.fixture.num_tokens):
267 + context = self._context(generated)
268 + skip_mask = self._skip_mask(generated, context)
269 + skipped = skip_mask is not None and bool(skip_mask.item())
270 + if skipped:
271 + dedup_skips += 1
272 + if len(generated) < self.scheme_config.context_width:
273 + partial_context_skips += 1
274 + routed_before = sampler.key_b_routed
275 + sample = watermarker.sample(
276 + self.fixture.logits(position),
277 + context,
278 + sampler,
279 + skip_mask,
280 + )
281 + if sampler.key_b_routed > routed_before and not skipped:
282 + key_b_effective += 1
283 + generated.append(int(sample.token_ids.item()))
284 +
285 + routes = self.scheme == "dual_key_gumbel" and (
286 + 0 < self.scheme_config.generation_alpha < 1
287 + )
288 + trace = GenerationTrace(
289 + routing_draws=sampler.routing_position,
290 + key_b_routed=sampler.key_b_routed if routes else None,
291 + key_b_effective=key_b_effective if routes else None,
292 + dedup_skips=dedup_skips,
293 + partial_context_skips=partial_context_skips,
294 + )
295 + return generated, trace
296 +
297 + def detect(self, token_ids: list[int]) -> WatermarkDetection:
298 + return self._detector().detect(token_ids)
299 +
300 + def golden_entry(
301 + self,
302 + detection_token_ids: list[int] | None = None,
303 + ) -> dict:
304 + """Generate, detect and trace this candidate into one golden entry.
305 +
306 + ``detection_token_ids`` runs detection over stored tokens instead of
307 + the freshly generated ones, so a token drift and a detection drift are
308 + reported independently.
309 + """
310 + token_ids, trace = self.generate_with_trace()
311 + scored = token_ids if detection_token_ids is None else detection_token_ids
312 + detection = self.detect(scored)
313 + threshold = self.scheme_config.p_value_threshold
314 + return {
315 + "configuration": self.configuration(),
316 + "resolved": self.resolved(),
317 + "generation": token_ids,
318 + "trace": _trace_payload(trace),
319 + "detection": {
320 + "score": detection.score,
321 + "p_value": detection.p_value,
322 + "p_value_ratio": detection.p_value / threshold,
323 + "num_scored_tokens": detection.num_scored_tokens,
324 + "is_watermarked": detection.is_watermarked,
325 + },
326 + }
327 +
328 + def _watermark_config(self) -> WatermarkConfig:
329 + return WatermarkConfig(
330 + algorithm=self.scheme,
331 + key=self.key,
332 + alpha=self.scheme_config.generation_alpha,
333 + context_width=self.scheme_config.context_width,
334 + deduplicate_contexts=(self.scheme_config.generation_deduplicate_contexts),
335 + deduplicate_contexts_max_history=(
336 + self.scheme_config.generation_deduplicate_contexts_max_history
337 + ),
338 + prf=self.prf,
339 + )
340 +
341 + def _detector(self) -> WatermarkDetector:
342 + return DETECTOR_FACTORIES[self.scheme](
343 + self.detection_key,
344 + self.scheme_config,
345 + self.prf,
346 + )
347 +
348 + def _context(self, generated: list[int]) -> torch.Tensor:
349 + width = self.scheme_config.context_width
350 + padded = [-1] * width + generated
351 + return torch.tensor([padded[-width:]], dtype=torch.int64)
352 +
353 + def _skip_mask(
354 + self, generated: list[int], context: torch.Tensor
355 + ) -> torch.Tensor | None:
356 + scope = self.scheme_config.generation_deduplicate_contexts
357 + if scope == "none":
358 + return None
359 +
360 + prompt_len = len(self.fixture.prompt)
361 + storage = torch.zeros(
362 + (1, prompt_len + self.fixture.num_tokens),
363 + dtype=torch.int64,
364 + )
365 + if prompt_len:
366 + storage[0, :prompt_len] = torch.tensor(self.fixture.prompt)
367 + if generated:
368 + storage[0, prompt_len : prompt_len + len(generated)] = torch.tensor(
369 + generated
370 + )
371 + return repeated_context_mask(
372 + storage,
373 + torch.tensor([0]),
374 + torch.tensor([prompt_len]),
375 + torch.tensor([prompt_len + len(generated)]),
376 + context,
377 + self.scheme_config.generation_deduplicate_contexts_max_history,
378 + include_prompt=scope == "all",
379 + skip_partial_context=scope == "all",
380 + )
381 +
382 +
383 +def _trace_payload(trace: GenerationTrace) -> dict:
384 + return {
385 + "routing_draws": trace.routing_draws,
386 + "key_b_routed": trace.key_b_routed,
387 + "key_b_effective": trace.key_b_effective,
388 + "dedup_skips": trace.dedup_skips,
389 + "partial_context_skips": trace.partial_context_skips,
390 + }
391 +
392 +
393 +BALANCED_FIXTURE = DeterministicGenerationFixture()
394 +
395 +# The first five tokens of BALANCED_FIXTURE under scope "all", so position 4 repeats.
396 +PROMPT_FIXTURE = DeterministicGenerationFixture(prompt=(46, 42, 38, 34, 122))
397 +
398 +REPETITIVE_FIXTURE = DeterministicGenerationFixture(
399 + vocabulary_size=16,
400 + num_tokens=48,
401 + dominant_token=3,
402 + dominant_bias=52,
403 +)
404 +
405 +MIDDLE_FIXTURE = DeterministicGenerationFixture(
406 + vocabulary_size=8,
407 + num_tokens=64,
408 + dominant_token=3,
409 + dominant_bias=16,
410 +)
411 +
412 +# Few enough scored tokens that the p_value lands just under its threshold.
413 +NEAR_THRESHOLD_FIXTURE = DeterministicGenerationFixture(
414 + vocabulary_size=16,
415 + num_tokens=8,
416 +)
417 +
418 +
419 +def _candidate(
420 + id: str,
421 + scheme: WatermarkingAlgorithm,
422 + key: int,
423 + *,
424 + prf: WatermarkPRFName,
425 + detection_key: int | None = None,
426 + context_width: int = 4,
427 + generation_alpha: float = 0.1,
428 + detection_alpha: float | None = None,
429 + generation_deduplicate_contexts: WatermarkContextScope = "single_turn",
430 + generation_deduplicate_contexts_max_history: int | None = 8192,
431 + detection_deduplicate_contexts: bool = True,
432 + p_value_threshold: float = 0.01,
433 + fixture: DeterministicGenerationFixture = BALANCED_FIXTURE,
434 +) -> WatermarkingCandidate:
435 + return WatermarkingCandidate(
436 + id=id,
437 + scheme=scheme,
438 + scheme_config=WatermarkingSchemeConfig(
439 + context_width=context_width,
440 + generation_alpha=generation_alpha,
441 + detection_alpha=(
442 + generation_alpha if detection_alpha is None else detection_alpha
443 + ),
444 + generation_deduplicate_contexts=generation_deduplicate_contexts,
445 + generation_deduplicate_contexts_max_history=(
446 + generation_deduplicate_contexts_max_history
447 + ),
448 + detection_deduplicate_contexts=detection_deduplicate_contexts,
449 + p_value_threshold=p_value_threshold,
450 + ),
451 + prf=prf,
452 + key=key,
453 + detection_key=key if detection_key is None else detection_key,
454 + fixture=fixture,
455 + )
456 +
457 +
458 +WATERMARKING_CANDIDATES = (
459 + _candidate("gumbel-philox-key0-cw1", "gumbel", 0, prf="philox", context_width=1),
460 + _candidate("gumbel-philox-key42-cw4", "gumbel", 42, prf="philox"),
461 + _candidate(
462 + "gumbel-philox-key42-cw4-wrong-key",
463 + "gumbel",
464 + 42,
465 + prf="philox",
466 + detection_key=43,
467 + ),
468 + _candidate(
469 + "gumbel-philox-key2p32-cw5-no-dedup",
470 + "gumbel",
471 + 2**32,
472 + prf="philox",
473 + context_width=5,
474 + generation_deduplicate_contexts="none",
475 + detection_deduplicate_contexts=False,
476 + ),
477 + _candidate(
478 + "gumbel-philox-keymax-cw16-all",
479 + "gumbel",
480 + 2**64 - 1,
481 + prf="philox",
482 + context_width=16,
483 + generation_deduplicate_contexts="all",
484 + ),
485 + _candidate(
486 + "gumbel-philox-key42-cw4-all",
487 + "gumbel",
488 + 42,
489 + prf="philox",
490 + generation_deduplicate_contexts="all",
491 + ),
492 + _candidate(
493 + "gumbel-philox-key42-cw4-all-prompt",
494 + "gumbel",
495 + 42,
496 + prf="philox",
497 + generation_deduplicate_contexts="all",
498 + fixture=PROMPT_FIXTURE,
499 + ),
500 + _candidate(
501 + "gumbel-philox-key42-cw4-mid-history8",
502 + "gumbel",
503 + 42,
504 + prf="philox",
505 + generation_deduplicate_contexts_max_history=8,
506 + fixture=MIDDLE_FIXTURE,
507 + ),
508 + _candidate(
509 + "gumbel-philox-key42-cw4-mid-history-none",
510 + "gumbel",
511 + 42,
512 + prf="philox",
513 + generation_deduplicate_contexts_max_history=None,
514 + fixture=MIDDLE_FIXTURE,
515 + ),
516 + _candidate(
517 + "gumbel-philox-key42-cw4-mid-no-dedup",
518 + "gumbel",
519 + 42,
520 + prf="philox",
521 + generation_deduplicate_contexts="none",
522 + generation_deduplicate_contexts_max_history=None,
523 + detection_deduplicate_contexts=False,
524 + fixture=MIDDLE_FIXTURE,
525 + ),
526 + _candidate(
527 + "gumbel-philox-key42-cw4-mid-all",
528 + "gumbel",
529 + 42,
530 + prf="philox",
531 + generation_deduplicate_contexts="all",
532 + generation_deduplicate_contexts_max_history=None,
533 + fixture=MIDDLE_FIXTURE,
534 + ),
535 + _candidate(
536 + "gumbel-philox-key42-cw4-near-threshold",
537 + "gumbel",
538 + 42,
539 + prf="philox",
540 + p_value_threshold=0.0015,
541 + fixture=NEAR_THRESHOLD_FIXTURE,
542 + ),
543 + _candidate(
544 + "gumbel-philox-repeated-cw1",
545 + "gumbel",
546 + 42,
547 + prf="philox",
548 + context_width=1,
549 + fixture=REPETITIVE_FIXTURE,
550 + ),
551 + _candidate(
552 + "gumbel-philox-repeated-cw4-all",
553 + "gumbel",
554 + 42,
555 + prf="philox",
556 + generation_deduplicate_contexts="all",
557 + fixture=REPETITIVE_FIXTURE,
558 + ),
559 + _candidate(
560 + "dual-key-gumbel-philox-key0-cw1-alpha0",
561 + "dual_key_gumbel",
562 + 0,
563 + prf="philox",
564 + context_width=1,
565 + generation_alpha=0.0,
566 + ),
567 + _candidate(
568 + "dual-key-gumbel-philox-key0-cw4-alpha0.1",
569 + "dual_key_gumbel",
570 + 0,
571 + prf="philox",
572 + ),
573 + _candidate(
574 + "dual-key-gumbel-philox-key42-cw4-alpha-mismatch",
575 + "dual_key_gumbel",
576 + 42,
577 + prf="philox",
578 + generation_alpha=0.1,
579 + detection_alpha=0.2,
580 + ),
581 + _candidate(
582 + "dual-key-gumbel-philox-key2p32-cw5-alpha0.5",
583 + "dual_key_gumbel",
584 + 2**32,
585 + prf="philox",
586 + context_width=5,
587 + generation_alpha=0.5,
588 + ),
589 + _candidate(
590 + "dual-key-gumbel-philox-keymax-cw16-alpha1",
591 + "dual_key_gumbel",
592 + 2**64 - 1,
593 + prf="philox",
594 + context_width=16,
595 + generation_alpha=1.0,
596 + ),
597 + _candidate(
598 + "dual-key-gumbel-philox-keymax-cw4-alpha0.4",
599 + "dual_key_gumbel",
600 + 2**64 - 1,
601 + prf="philox",
602 + generation_alpha=0.4,
603 + ),
604 + _candidate(
605 + "dual-key-gumbel-philox-key7-cw4-near-threshold",
606 + "dual_key_gumbel",
607 + 7,
608 + prf="philox",
609 + p_value_threshold=0.003,
610 + fixture=NEAR_THRESHOLD_FIXTURE,
611 + ),
612 + _candidate(
613 + "dual-key-gumbel-philox-repeated-cw1",
614 + "dual_key_gumbel",
615 + 42,
616 + prf="philox",
617 + context_width=1,
618 + fixture=REPETITIVE_FIXTURE,
619 + ),
620 + _candidate(
621 + "dual-key-gumbel-philox-repeated-cw4-all",
622 + "dual_key_gumbel",
623 + 42,
624 + prf="philox",
625 + generation_deduplicate_contexts="all",
626 + fixture=REPETITIVE_FIXTURE,
627 + ),
628 +)
629 +
630 +DEDUPLICATION_TWINS = (
631 + (
632 + "gumbel-philox-key42-cw4-mid-history-none",
633 + "gumbel-philox-key42-cw4-mid-no-dedup",
634 + ),
635 +)
636 +
637 +
638 +def validate_golden_guards(entries: Mapping[str, dict]) -> None:
639 + candidates_by_id = {
640 + candidate.id: candidate for candidate in WATERMARKING_CANDIDATES
641 + }
642 + combinations = {
643 + (candidate.scheme, candidate.prf) for candidate in WATERMARKING_CANDIDATES
644 + }
645 + expected_combinations = configured_algorithm_prf_combinations()
646 + if combinations != expected_combinations:
647 + missing_combinations = sorted(expected_combinations - combinations)
648 + unexpected_combinations = sorted(combinations - expected_combinations)
649 + raise GoldenGuardError(
650 + "candidate scheme/PRF coverage mismatch: "
651 + f"missing {missing_combinations}, unexpected {unexpected_combinations}"
652 + )
653 +
654 + missing = sorted(set(candidates_by_id) - set(entries))
655 + if missing:
656 + raise GoldenGuardError(f"golden entries are missing candidates: {missing}")
657 +
658 + for candidate in WATERMARKING_CANDIDATES:
659 + entry = entries[candidate.id]
660 + ratio = entry["detection"]["p_value_ratio"]
661 + if abs(ratio - 1.0) < P_VALUE_RATIO_MIN_DISTANCE:
662 + raise GoldenGuardError(
663 + f"{candidate.id}: p_value is within "
664 + f"{P_VALUE_RATIO_MIN_DISTANCE:g} of p_value_threshold "
665 + f"(ratio {ratio!r}), so is_watermarked is not reproducible"
666 + )
667 +
668 + if candidate.fixture == REPETITIVE_FIXTURE:
669 + alternate_tokens = replace(candidate, key=candidate.key ^ 1).generate()
670 + changed_tokens = sum(
671 + actual != alternate
672 + for actual, alternate in zip(
673 + entry["generation"], alternate_tokens, strict=True
674 + )
675 + )
676 + if changed_tokens < REPETITIVE_MIN_KEY_PERTURBATION_TOKENS:
677 + raise GoldenGuardError(
678 + f"{candidate.id}: key ^ 1 changes only {changed_tokens} tokens; "
679 + f"expected at least {REPETITIVE_MIN_KEY_PERTURBATION_TOKENS}"
680 + )
681 + full_context_skips = (
682 + entry["trace"]["dedup_skips"] - entry["trace"]["partial_context_skips"]
683 + )
684 + if full_context_skips < 1:
685 + raise GoldenGuardError(
686 + f"{candidate.id}: repetitive fixture has no full-context "
687 + "deduplication skip"
688 + )
689 + if ratio > REPETITIVE_MAX_P_VALUE_RATIO:
690 + raise GoldenGuardError(
691 + f"{candidate.id}: p_value_ratio {ratio!r} exceeds "
692 + f"{REPETITIVE_MAX_P_VALUE_RATIO}"
693 + )
694 +
695 + alpha = candidate.scheme_config.generation_alpha
696 + if candidate.scheme == "dual_key_gumbel" and 0 < alpha < 1:
697 + boundary = routing_boundary(alpha)
698 + distance = min(
699 + abs(uniform - boundary)
700 + for uniform in candidate.fixture.routing_uniforms
701 + )
702 + if distance < ROUTING_BOUNDARY_MIN_DISTANCE:
703 + raise GoldenGuardError(
704 + f"{candidate.id}: routing uniform is only {distance!r} from "
705 + f"the realised boundary; expected at least "
706 + f"{ROUTING_BOUNDARY_MIN_DISTANCE:g}"
707 + )
708 +
709 + for enabled_id, disabled_id in DEDUPLICATION_TWINS:
710 + enabled = candidates_by_id[enabled_id]
711 + disabled = candidates_by_id[disabled_id]
712 + expected_disabled = replace(
713 + enabled,
714 + id=disabled_id,
715 + scheme_config=replace(
716 + enabled.scheme_config,
717 + generation_deduplicate_contexts="none",
718 + detection_deduplicate_contexts=False,
719 + ),
720 + )
721 + if disabled != expected_disabled:
722 + raise GoldenGuardError(
723 + f"{enabled_id} and {disabled_id} must differ only in generation "
724 + "and detection deduplication"
725 + )
726 +
727 + enabled_entry = entries[enabled_id]
728 + disabled_entry = entries[disabled_id]
729 + if enabled_entry["trace"]["dedup_skips"] < 1:
730 + raise GoldenGuardError(f"{enabled_id}: expected at least one dedup skip")
731 + if disabled_entry["trace"]["dedup_skips"] != 0:
732 + raise GoldenGuardError(f"{disabled_id}: expected zero dedup skips")
733 + if enabled_entry["generation"] == disabled_entry["generation"]:
734 + raise GoldenGuardError(
735 + f"{enabled_id} and {disabled_id}: generations must differ"
736 + )
737 + enabled_scored = enabled_entry["detection"]["num_scored_tokens"]
738 + disabled_scored = disabled_entry["detection"]["num_scored_tokens"]
739 + if enabled_scored == disabled_scored:
740 + raise GoldenGuardError(
741 + f"{enabled_id} and {disabled_id}: detector scored-token counts "
742 + "must differ"
743 + )
744 +
745 +
746 +def golden_payload() -> dict:
747 + candidates = {
748 + candidate.id: candidate.golden_entry() for candidate in WATERMARKING_CANDIDATES
749 + }
750 + return {
751 + "schema_version": GOLDEN_SCHEMA_VERSION,
752 + "candidates": candidates,
753 + }
754 +
755 +
756 +def floats_match(actual: float, expected: float) -> bool:
757 + return math.isclose(
758 + actual,
759 + expected,
760 + rel_tol=GOLDEN_FLOAT_RTOL,
761 + abs_tol=GOLDEN_FLOAT_ATOL,
762 + )
763 +
764 +
765 +def _relative_difference(actual: float, expected: float) -> float:
766 + if expected == 0.0:
767 + return 0.0 if actual == 0.0 else math.inf
768 + return abs(actual - expected) / abs(expected)
769 +
770 +
771 +def _compare_float(
772 + field: str,
773 + actual: float,
774 + expected: float,
775 + differences: list[str],
776 +) -> None:
777 + if not floats_match(actual, expected):
778 + differences.append(
779 + f"{field}: expected {expected!r}, got {actual!r}, "
780 + f"relative difference {_relative_difference(actual, expected):.3e}"
781 + )
782 +
783 +
784 +def _compare_exact(
785 + field: str,
786 + actual: object,
787 + expected: object,
788 + differences: list[str],
789 +) -> None:
790 + if actual != expected:
791 + differences.append(f"{field}: expected {expected!r}, got {actual!r}")
792 +
793 +
794 +def _compare_mapping(
795 + field: str,
796 + actual: Mapping[str, object],
797 + expected: Mapping[str, object],
798 + differences: list[str],
799 +) -> None:
800 + for key in sorted(set(actual) | set(expected)):
801 + if actual.get(key) != expected.get(key):
802 + differences.append(
803 + f"{field}.{key}: expected {expected.get(key)!r}, "
804 + f"got {actual.get(key)!r}"
805 + )
806 +
807 +
808 +def compare_entries(produced: dict, golden: dict) -> list[str]:
809 + """Return one line per field that differs, empty when the two agree.
810 +
811 + Configuration, resolved state, tokens, trace,
812 + num_scored_tokens and is_watermarked are compared exactly; score, p_value
813 + and p_value_ratio are compared with GOLDEN_FLOAT_RTOL.
814 + """
815 + differences: list[str] = []
816 + for field in ("configuration", "resolved", "trace"):
817 + _compare_mapping(field, produced[field], golden[field], differences)
818 +
819 + if produced["generation"] != golden["generation"]:
820 + first_difference = next(
821 + (
822 + index
823 + for index, (actual, expected) in enumerate(
824 + zip(produced["generation"], golden["generation"])
825 + )
826 + if actual != expected
827 + ),
828 + min(len(produced["generation"]), len(golden["generation"])),
829 + )
830 + differences.append(
831 + f"generation: first differs at index {first_difference} "
832 + f"({len(produced['generation'])} tokens generated, "
833 + f"{len(golden['generation'])} in the golden): "
834 + f"expected {golden['generation']}, got {produced['generation']}"
835 + )
836 +
837 + detection = produced["detection"]
838 + expected_detection = golden["detection"]
839 + for field in ("score", "p_value", "p_value_ratio"):
840 + _compare_float(
841 + f"detection.{field}",
842 + detection[field],
843 + expected_detection[field],
844 + differences,
845 + )
846 + for field in ("num_scored_tokens", "is_watermarked"):
847 + _compare_exact(
848 + f"detection.{field}",
849 + detection[field],
850 + expected_detection[field],
851 + differences,
852 + )
853 + return differences
854 +
855 +
856 +def compare_golden(candidate: WatermarkingCandidate, golden: dict) -> list[str]:
857 + """Return one line per field of ``candidate`` that drifted from ``golden``."""
858 + return compare_entries(candidate.golden_entry(golden["generation"]), golden)
859 +
860 +
861 +def read_goldens(path: Path) -> dict[str, dict]:
862 + """Read the goldens file and return its candidate mapping."""
863 + payload = json.loads(path.read_text(encoding="utf-8"))
864 + version = payload.get("schema_version")
865 + if version != GOLDEN_SCHEMA_VERSION:
866 + raise GoldenFormatError(
867 + f"{path}: schema_version {version!r}, expected {GOLDEN_SCHEMA_VERSION}"
868 + )
869 + return payload["candidates"]
870 +
871 +
872 +def configured_algorithms() -> set[str]:
873 + return set(get_args(WatermarkingAlgorithm))
874 +
875 +
876 +def configured_prfs() -> set[str]:
877 + return set(get_args(WatermarkPRFName))
878 +
879 +
880 +def configured_algorithm_prf_combinations() -> set[tuple[str, str]]:
881 + return {
882 + (algorithm, prf)
883 + for algorithm in configured_algorithms()
884 + for prf in configured_prfs()
885 + }
tests/watermarking/test_goldens.py new
+99
@@ -0,0 +1,99 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +
4 +"""Check the watermarking goldens still reproduce."""
5 +
6 +import dataclasses
7 +from pathlib import Path
8 +
9 +import pytest
10 +
11 +from tests.watermarking.golden_candidates import (
12 + DETECTOR_FACTORIES,
13 + GOLDEN_FLOAT_RTOL,
14 + WATERMARK_CONFIG_FIELDS,
15 + WATERMARKING_CANDIDATES,
16 + compare_golden,
17 + configured_algorithm_prf_combinations,
18 + configured_algorithms,
19 + read_goldens,
20 + routing_boundary,
21 + validate_golden_guards,
22 +)
23 +from vllm.config.watermarking import WatermarkConfig
24 +
25 +GOLDENS_PATH = Path(__file__).with_name("watermarking_goldens.json")
26 +
27 +CANDIDATES_BY_ID = {candidate.id: candidate for candidate in WATERMARKING_CANDIDATES}
28 +
29 +NEGATIVE_CANDIDATE_ID = "gumbel-philox-key42-cw4-wrong-key"
30 +
31 +
32 +@pytest.fixture(scope="module")
33 +def goldens() -> dict[str, dict]:
34 + return read_goldens(GOLDENS_PATH)
35 +
36 +
37 +def test_goldens_cover_every_candidate(goldens: dict[str, dict]):
38 + candidate_ids = set(CANDIDATES_BY_ID)
39 +
40 + assert len(candidate_ids) == len(WATERMARKING_CANDIDATES)
41 + assert set(goldens) == candidate_ids, "golden contract does not match candidates"
42 + candidate_combinations = {
43 + (candidate.scheme, candidate.prf) for candidate in WATERMARKING_CANDIDATES
44 + }
45 + assert candidate_combinations == configured_algorithm_prf_combinations()
46 + assert set(DETECTOR_FACTORIES) == configured_algorithms()
47 +
48 +
49 +def test_golden_fixture_guards(goldens: dict[str, dict]):
50 + validate_golden_guards(goldens)
51 +
52 +
53 +def test_resolved_records_every_watermark_config_field():
54 + assert {field.name for field in dataclasses.fields(WatermarkConfig)} == (
55 + set(WATERMARK_CONFIG_FIELDS) | {"key"}
56 + )
57 +
58 +
59 +@pytest.mark.parametrize(
60 + "candidate",
61 + WATERMARKING_CANDIDATES,
62 + ids=lambda candidate: candidate.id,
63 +)
64 +def test_watermarking_candidate_golden(candidate, goldens: dict[str, dict]):
65 + differences = compare_golden(candidate, goldens[candidate.id])
66 +
67 + assert not differences, "\n".join(
68 + [
69 + f"{candidate.id}: golden drift "
70 + f"(tolerance {GOLDEN_FLOAT_RTOL:g} on score and p_value)",
71 + *differences,
72 + ]
73 + )
74 +
75 +
76 +def test_negative_row_is_not_watermarked(goldens: dict[str, dict]):
77 + candidate = CANDIDATES_BY_ID[NEGATIVE_CANDIDATE_ID]
78 +
79 + assert candidate.detection_key != candidate.key
80 + assert goldens[NEGATIVE_CANDIDATE_ID]["detection"]["is_watermarked"] is False
81 +
82 +
83 +@pytest.mark.parametrize(
84 + "candidate",
85 + [
86 + candidate
87 + for candidate in WATERMARKING_CANDIDATES
88 + if candidate.scheme == "dual_key_gumbel"
89 + and 0 < candidate.scheme_config.generation_alpha < 1
90 + ],
91 + ids=lambda candidate: candidate.id,
92 +)
93 +def test_routing_sequence_resolves_alpha(candidate):
94 + alpha = candidate.scheme_config.generation_alpha
95 + uniforms = candidate.fixture.routing_uniforms
96 + boundary = routing_boundary(alpha)
97 + share = sum(1 for uniform in uniforms if boundary <= uniform) / len(uniforms)
98 +
99 + assert abs(share - alpha) <= 0.03
tests/watermarking/test_watermarking.py
+6 -3
@@ -276,9 +276,9 @@ def test_gpu_sampler_warns_when_watermarking_is_enabled_for_greedy(monkeypatch):
276 "vllm.v1.watermarking.gpu_sampler.logger.warning_once", messages.append
277 )
278
279 - sampler.add_request(0, 1, SamplingParams(temperature=0))
280 - sampler.add_request(0, 1, SamplingParams(temperature=1))
281 - sampler.add_request(0, 1, SamplingParams(temperature=0, watermarking=False))
279 + sampler.add_request(0, SamplingParams(temperature=0))
280 + sampler.add_request(0, SamplingParams(temperature=1))
281 + sampler.add_request(0, SamplingParams(temperature=0, watermarking=False))
282
283 assert messages == [
284 (
@@ -945,6 +945,7 @@ def test_draft_sampler_uses_draft_key_and_advances_context(monkeypatch):
945 speculator = object.__new__(StubSpeculator)
946 speculator.model = StubModel()
947 speculator.use_fp64_gumbel = False
948 + speculator.acceptance_estimator = None
949 draft_watermarker = object.__new__(DraftWatermarker)
950 draft_watermarker.watermarker = StubWatermarker()
951 draft_watermarker.contexts = torch.tensor([[1, 2], [3, 4]])
@@ -978,6 +979,7 @@ def test_dspark_reduced_vocab_draft_sampler_applies_watermarking(monkeypatch):
979 speculator.seeds = torch.zeros(2, dtype=torch.int64)
980 speculator._step_cols = torch.tensor([0])
981 speculator.use_fp64_gumbel = False
982 + speculator.acceptance_estimator = None
983 watermark_logits: list[torch.Tensor] = []
984
985 def sample(logits, sampled, idx_map, temperature):
@@ -1012,6 +1014,7 @@ def test_dspark_target_only_watermarking_leaves_drafts_unwatermarked(monkeypatch
1014 speculator.seeds = torch.zeros(2, dtype=torch.int64)
1015 speculator._step_cols = torch.tensor([0])
1016 speculator.use_fp64_gumbel = False
1017 + speculator.acceptance_estimator = None
1018 speculator.draft_watermarker = None
1019 monkeypatch.setattr(
1020 "vllm.v1.worker.gpu.spec_decode.dspark.speculator.gumbel_sample",
tests/watermarking/watermarking_goldens.json new
+2848
@@ -0,0 +1,2848 @@
1 +{
2 + "candidates": {
3 + "dual-key-gumbel-philox-key0-cw1-alpha0": {
4 + "configuration": {
5 + "detection_key": "0",
6 + "fixture": {
7 + "dominant_bias": 0,
8 + "dominant_token": null,
9 + "logit_denominator": 16,
10 + "logit_modulus": 131,
11 + "num_tokens": 64,
12 + "prompt": [],
13 + "vocabulary_size": 128
14 + },
15 + "key": "0",
16 + "prf": "philox",
17 + "prf_version": "philox4x32-10-v1",
18 + "scheme": "dual_key_gumbel",
19 + "scheme_config": {
20 + "context_width": 1,
21 + "detection_alpha": 0.0,
22 + "detection_deduplicate_contexts": true,
23 + "generation_alpha": 0.0,
24 + "generation_deduplicate_contexts": "single_turn",
25 + "generation_deduplicate_contexts_max_history": 8192,
26 + "p_value_threshold": 0.01
27 + }
28 + },
29 + "detection": {
30 + "is_watermarked": true,
31 + "num_scored_tokens": 55,
32 + "p_value": 2.7410983620034455e-44,
33 + "p_value_ratio": 2.7410983620034455e-42,
34 + "score": 229.87923167230588
35 + },
36 + "generation": [
37 + 81,
38 + 91,
39 + 84,
40 + 122,
41 + 37,
42 + 118,
43 + 11,
44 + 3,
45 + 116,
46 + 56,
47 + 66,
48 + 37,
49 + 44,
50 + 40,
51 + 85,
52 + 31,
53 + 102,
54 + 73,
55 + 126,
56 + 16,
57 + 40,
58 + 93,
59 + 11,
60 + 85,
61 + 81,
62 + 77,
63 + 41,
64 + 12,
65 + 118,
66 + 61,
67 + 57,
68 + 53,
69 + 38,
70 + 111,
71 + 115,
72 + 5,
73 + 1,
74 + 99,
75 + 113,
76 + 28,
77 + 87,
78 + 55,
79 + 83,
80 + 51,
81 + 47,
82 + 57,
83 + 124,
84 + 31,
85 + 116,
86 + 112,
87 + 30,
88 + 43,
89 + 89,
90 + 110,
91 + 60,
92 + 10,
93 + 9,
94 + 58,
95 + 114,
96 + 125,
97 + 68,
98 + 78,
99 + 106,
100 + 56
101 + ],
102 + "resolved": {
103 + "derived_keys": {
104 + "key_a": "15691511769010696906",
105 + "key_b": "9736202488081271922"
106 + },
107 + "detector": {
108 + "alpha": 0.0,
109 + "context_width": 1,
110 + "deduplicate_contexts": true,
111 + "key_b_prf_key": "9736202488081271922",
112 + "p_value_threshold": 0.01,
113 + "prf_key": "15691511769010696906",
114 + "type": "DualKeyGumbelWatermarkDetector"
115 + },
116 + "watermark_config": {
117 + "algorithm": "dual_key_gumbel",
118 + "allow_target_only_watermarking": false,
119 + "alpha": 0.0,
120 + "context_width": 1,
121 + "deduplicate_contexts": "single_turn",
122 + "deduplicate_contexts_max_history": 8192,
123 + "prf": "philox"
124 + }
125 + },
126 + "trace": {
127 + "dedup_skips": 9,
128 + "key_b_effective": null,
129 + "key_b_routed": null,
130 + "partial_context_skips": 0,
131 + "routing_draws": 0
132 + }
133 + },
134 + "dual-key-gumbel-philox-key0-cw4-alpha0.1": {
135 + "configuration": {
136 + "detection_key": "0",
137 + "fixture": {
138 + "dominant_bias": 0,
139 + "dominant_token": null,
140 + "logit_denominator": 16,
141 + "logit_modulus": 131,
142 + "num_tokens": 64,
143 + "prompt": [],
144 + "vocabulary_size": 128
145 + },
146 + "key": "0",
147 + "prf": "philox",
148 + "prf_version": "philox4x32-10-v1",
149 + "scheme": "dual_key_gumbel",
150 + "scheme_config": {
151 + "context_width": 4,
152 + "detection_alpha": 0.1,
153 + "detection_deduplicate_contexts": true,
154 + "generation_alpha": 0.1,
155 + "generation_deduplicate_contexts": "single_turn",
156 + "generation_deduplicate_contexts_max_history": 8192,
157 + "p_value_threshold": 0.01
158 + }
159 + },
160 + "detection": {
161 + "is_watermarked": true,
162 + "num_scored_tokens": 64,
163 + "p_value": 1.234132682420224e-51,
164 + "p_value_ratio": 1.234132682420224e-49,
165 + "score": 241.81126265648692
166 + },
167 + "generation": [
168 + 70,
169 + 56,
170 + 91,
171 + 90,
172 + 72,
173 + 68,
174 + 17,
175 + 78,
176 + 21,
177 + 116,
178 + 104,
179 + 62,
180 + 15,
181 + 100,
182 + 82,
183 + 13,
184 + 88,
185 + 70,
186 + 112,
187 + 62,
188 + 79,
189 + 57,
190 + 11,
191 + 113,
192 + 88,
193 + 80,
194 + 23,
195 + 90,
196 + 79,
197 + 42,
198 + 18,
199 + 60,
200 + 10,
201 + 73,
202 + 94,
203 + 64,
204 + 96,
205 + 71,
206 + 35,
207 + 56,
208 + 38,
209 + 93,
210 + 37,
211 + 54,
212 + 82,
213 + 127,
214 + 0,
215 + 45,
216 + 37,
217 + 75,
218 + 19,
219 + 92,
220 + 68,
221 + 127,
222 + 28,
223 + 56,
224 + 45,
225 + 69,
226 + 8,
227 + 1,
228 + 110,
229 + 39,
230 + 127,
231 + 56
232 + ],
233 + "resolved": {
234 + "derived_keys": {
235 + "key_a": "15691511769010696906",
236 + "key_b": "9736202488081271922"
237 + },
238 + "detector": {
239 + "alpha": 0.1,
240 + "context_width": 4,
241 + "deduplicate_contexts": true,
242 + "key_b_prf_key": "9736202488081271922",
243 + "p_value_threshold": 0.01,
244 + "prf_key": "15691511769010696906",
245 + "type": "DualKeyGumbelWatermarkDetector"
246 + },
247 + "watermark_config": {
248 + "algorithm": "dual_key_gumbel",
249 + "allow_target_only_watermarking": false,
250 + "alpha": 0.1,
251 + "context_width": 4,
252 + "deduplicate_contexts": "single_turn",
253 + "deduplicate_contexts_max_history": 8192,
254 + "prf": "philox"
255 + }
256 + },
257 + "trace": {
258 + "dedup_skips": 0,
259 + "key_b_effective": 7,
260 + "key_b_routed": 7,
261 + "partial_context_skips": 0,
262 + "routing_draws": 64
263 + }
264 + },
265 + "dual-key-gumbel-philox-key2p32-cw5-alpha0.5": {
266 + "configuration": {
267 + "detection_key": "4294967296",
268 + "fixture": {
269 + "dominant_bias": 0,
270 + "dominant_token": null,
271 + "logit_denominator": 16,
272 + "logit_modulus": 131,
273 + "num_tokens": 64,
274 + "prompt": [],
275 + "vocabulary_size": 128
276 + },
277 + "key": "4294967296",
278 + "prf": "philox",
279 + "prf_version": "philox4x32-10-v1",
280 + "scheme": "dual_key_gumbel",
281 + "scheme_config": {
282 + "context_width": 5,
283 + "detection_alpha": 0.5,
284 + "detection_deduplicate_contexts": true,
285 + "generation_alpha": 0.5,
286 + "generation_deduplicate_contexts": "single_turn",
287 + "generation_deduplicate_contexts_max_history": 8192,
288 + "p_value_threshold": 0.01
289 + }
290 + },
291 + "detection": {
292 + "is_watermarked": true,
293 + "num_scored_tokens": 64,
294 + "p_value": 6.733621085489817e-36,
295 + "p_value_ratio": 6.7336210854898174e-34,
296 + "score": 162.0665949787407
297 + },
298 + "generation": [
299 + 53,
300 + 116,
301 + 48,
302 + 94,
303 + 47,
304 + 118,
305 + 64,
306 + 123,
307 + 106,
308 + 65,
309 + 59,
310 + 48,
311 + 4,
312 + 86,
313 + 64,
314 + 32,
315 + 84,
316 + 116,
317 + 23,
318 + 23,
319 + 65,
320 + 15,
321 + 50,
322 + 67,
323 + 49,
324 + 69,
325 + 34,
326 + 76,
327 + 1,
328 + 95,
329 + 103,
330 + 120,
331 + 102,
332 + 69,
333 + 125,
334 + 121,
335 + 57,
336 + 39,
337 + 127,
338 + 113,
339 + 98,
340 + 87,
341 + 9,
342 + 51,
343 + 100,
344 + 53,
345 + 46,
346 + 42,
347 + 123,
348 + 55,
349 + 108,
350 + 100,
351 + 50,
352 + 103,
353 + 67,
354 + 80,
355 + 94,
356 + 41,
357 + 97,
358 + 117,
359 + 18,
360 + 66,
361 + 74,
362 + 38
363 + ],
364 + "resolved": {
365 + "derived_keys": {
366 + "key_a": "9864452095710749384",
367 + "key_b": "10834826187781349534"
368 + },
369 + "detector": {
370 + "alpha": 0.5,
371 + "context_width": 5,
372 + "deduplicate_contexts": true,
373 + "key_b_prf_key": "10834826187781349534",
374 + "p_value_threshold": 0.01,
375 + "prf_key": "9864452095710749384",
376 + "type": "DualKeyGumbelWatermarkDetector"
377 + },
378 + "watermark_config": {
379 + "algorithm": "dual_key_gumbel",
380 + "allow_target_only_watermarking": false,
381 + "alpha": 0.5,
382 + "context_width": 5,
383 + "deduplicate_contexts": "single_turn",
384 + "deduplicate_contexts_max_history": 8192,
385 + "prf": "philox"
386 + }
387 + },
388 + "trace": {
389 + "dedup_skips": 0,
390 + "key_b_effective": 33,
391 + "key_b_routed": 33,
392 + "partial_context_skips": 0,
393 + "routing_draws": 64
394 + }
395 + },
396 + "dual-key-gumbel-philox-key42-cw4-alpha-mismatch": {
397 + "configuration": {
398 + "detection_key": "42",
399 + "fixture": {
400 + "dominant_bias": 0,
401 + "dominant_token": null,
402 + "logit_denominator": 16,
403 + "logit_modulus": 131,
404 + "num_tokens": 64,
405 + "prompt": [],
406 + "vocabulary_size": 128
407 + },
408 + "key": "42",
409 + "prf": "philox",
410 + "prf_version": "philox4x32-10-v1",
411 + "scheme": "dual_key_gumbel",
412 + "scheme_config": {
413 + "context_width": 4,
414 + "detection_alpha": 0.2,
415 + "detection_deduplicate_contexts": true,
416 + "generation_alpha": 0.1,
417 + "generation_deduplicate_contexts": "single_turn",
418 + "generation_deduplicate_contexts_max_history": 8192,
419 + "p_value_threshold": 0.01
420 + }
421 + },
422 + "detection": {
423 + "is_watermarked": true,
424 + "num_scored_tokens": 64,
425 + "p_value": 1.6569954931292397e-49,
426 + "p_value_ratio": 1.6569954931292398e-47,
427 + "score": 215.24386344560963
428 + },
429 + "generation": [
430 + 46,
431 + 56,
432 + 6,
433 + 122,
434 + 30,
435 + 100,
436 + 121,
437 + 67,
438 + 63,
439 + 116,
440 + 13,
441 + 65,
442 + 90,
443 + 47,
444 + 25,
445 + 88,
446 + 120,
447 + 31,
448 + 2,
449 + 37,
450 + 40,
451 + 34,
452 + 92,
453 + 28,
454 + 45,
455 + 126,
456 + 94,
457 + 90,
458 + 18,
459 + 36,
460 + 74,
461 + 102,
462 + 10,
463 + 13,
464 + 101,
465 + 51,
466 + 29,
467 + 29,
468 + 46,
469 + 81,
470 + 63,
471 + 2,
472 + 29,
473 + 78,
474 + 121,
475 + 89,
476 + 124,
477 + 109,
478 + 123,
479 + 34,
480 + 30,
481 + 93,
482 + 4,
483 + 110,
484 + 28,
485 + 34,
486 + 105,
487 + 41,
488 + 5,
489 + 48,
490 + 81,
491 + 120,
492 + 60,
493 + 70
494 + ],
495 + "resolved": {
496 + "derived_keys": {
497 + "key_a": "37150756557148556",
498 + "key_b": "15826636237805488045"
499 + },
500 + "detector": {
501 + "alpha": 0.2,
502 + "context_width": 4,
503 + "deduplicate_contexts": true,
504 + "key_b_prf_key": "15826636237805488045",
505 + "p_value_threshold": 0.01,
506 + "prf_key": "37150756557148556",
507 + "type": "DualKeyGumbelWatermarkDetector"
508 + },
509 + "watermark_config": {
510 + "algorithm": "dual_key_gumbel",
511 + "allow_target_only_watermarking": false,
512 + "alpha": 0.1,
513 + "context_width": 4,
514 + "deduplicate_contexts": "single_turn",
515 + "deduplicate_contexts_max_history": 8192,
516 + "prf": "philox"
517 + }
518 + },
519 + "trace": {
520 + "dedup_skips": 0,
521 + "key_b_effective": 7,
522 + "key_b_routed": 7,
523 + "partial_context_skips": 0,
524 + "routing_draws": 64
525 + }
526 + },
527 + "dual-key-gumbel-philox-key7-cw4-near-threshold": {
528 + "configuration": {
529 + "detection_key": "7",
530 + "fixture": {
531 + "dominant_bias": 0,
532 + "dominant_token": null,
533 + "logit_denominator": 16,
534 + "logit_modulus": 131,
535 + "num_tokens": 8,
536 + "prompt": [],
537 + "vocabulary_size": 16
538 + },
539 + "key": "7",
540 + "prf": "philox",
541 + "prf_version": "philox4x32-10-v1",
542 + "scheme": "dual_key_gumbel",
543 + "scheme_config": {
544 + "context_width": 4,
545 + "detection_alpha": 0.1,
546 + "detection_deduplicate_contexts": true,
547 + "generation_alpha": 0.1,
548 + "generation_deduplicate_contexts": "single_turn",
549 + "generation_deduplicate_contexts_max_history": 8192,
550 + "p_value_threshold": 0.003
551 + }
552 + },
553 + "detection": {
554 + "is_watermarked": true,
555 + "num_scored_tokens": 8,
556 + "p_value": 0.002130807652336366,
557 + "p_value_ratio": 0.7102692174454553,
558 + "score": 17.2829982488662
559 + },
560 + "generation": [
561 + 7,
562 + 3,
563 + 13,
564 + 2,
565 + 5,
566 + 1,
567 + 4,
568 + 14
569 + ],
570 + "resolved": {
571 + "derived_keys": {
572 + "key_a": "3490214442843822077",
573 + "key_b": "7720047515392181251"
574 + },
575 + "detector": {
576 + "alpha": 0.1,
577 + "context_width": 4,
578 + "deduplicate_contexts": true,
579 + "key_b_prf_key": "7720047515392181251",
580 + "p_value_threshold": 0.003,
581 + "prf_key": "3490214442843822077",
582 + "type": "DualKeyGumbelWatermarkDetector"
583 + },
584 + "watermark_config": {
585 + "algorithm": "dual_key_gumbel",
586 + "allow_target_only_watermarking": false,
587 + "alpha": 0.1,
588 + "context_width": 4,
589 + "deduplicate_contexts": "single_turn",
590 + "deduplicate_contexts_max_history": 8192,
591 + "prf": "philox"
592 + }
593 + },
594 + "trace": {
595 + "dedup_skips": 0,
596 + "key_b_effective": 1,
597 + "key_b_routed": 1,
598 + "partial_context_skips": 0,
599 + "routing_draws": 8
600 + }
601 + },
602 + "dual-key-gumbel-philox-keymax-cw16-alpha1": {
603 + "configuration": {
604 + "detection_key": "18446744073709551615",
605 + "fixture": {
606 + "dominant_bias": 0,
607 + "dominant_token": null,
608 + "logit_denominator": 16,
609 + "logit_modulus": 131,
610 + "num_tokens": 64,
611 + "prompt": [],
612 + "vocabulary_size": 128
613 + },
614 + "key": "18446744073709551615",
615 + "prf": "philox",
616 + "prf_version": "philox4x32-10-v1",
617 + "scheme": "dual_key_gumbel",
618 + "scheme_config": {
619 + "context_width": 16,
620 + "detection_alpha": 1.0,
621 + "detection_deduplicate_contexts": true,
622 + "generation_alpha": 1.0,
623 + "generation_deduplicate_contexts": "single_turn",
624 + "generation_deduplicate_contexts_max_history": 8192,
625 + "p_value_threshold": 0.01
626 + }
627 + },
628 + "detection": {
629 + "is_watermarked": true,
630 + "num_scored_tokens": 64,
631 + "p_value": 2.5582243247165326e-52,
632 + "p_value_ratio": 2.558224324716533e-50,
633 + "score": 270.9771333793902
634 + },
635 + "generation": [
636 + 45,
637 + 3,
638 + 73,
639 + 41,
640 + 47,
641 + 40,
642 + 21,
643 + 85,
644 + 80,
645 + 70,
646 + 34,
647 + 48,
648 + 58,
649 + 47,
650 + 96,
651 + 28,
652 + 56,
653 + 77,
654 + 101,
655 + 30,
656 + 97,
657 + 103,
658 + 96,
659 + 53,
660 + 87,
661 + 37,
662 + 34,
663 + 113,
664 + 33,
665 + 25,
666 + 103,
667 + 113,
668 + 102,
669 + 6,
670 + 101,
671 + 37,
672 + 29,
673 + 75,
674 + 46,
675 + 67,
676 + 105,
677 + 48,
678 + 115,
679 + 51,
680 + 114,
681 + 4,
682 + 0,
683 + 81,
684 + 98,
685 + 35,
686 + 115,
687 + 111,
688 + 7,
689 + 78,
690 + 21,
691 + 52,
692 + 112,
693 + 48,
694 + 19,
695 + 47,
696 + 68,
697 + 17,
698 + 106,
699 + 73
700 + ],
701 + "resolved": {
702 + "derived_keys": {
703 + "key_a": "11075965703501209888",
704 + "key_b": "7399018451426338514"
705 + },
706 + "detector": {
707 + "alpha": 1.0,
708 + "context_width": 16,
709 + "deduplicate_contexts": true,
710 + "key_b_prf_key": "7399018451426338514",
711 + "p_value_threshold": 0.01,
712 + "prf_key": "11075965703501209888",
713 + "type": "DualKeyGumbelWatermarkDetector"
714 + },
715 + "watermark_config": {
716 + "algorithm": "dual_key_gumbel",
717 + "allow_target_only_watermarking": false,
718 + "alpha": 1.0,
719 + "context_width": 16,
720 + "deduplicate_contexts": "single_turn",
721 + "deduplicate_contexts_max_history": 8192,
722 + "prf": "philox"
723 + }
724 + },
725 + "trace": {
726 + "dedup_skips": 0,
727 + "key_b_effective": null,
728 + "key_b_routed": null,
729 + "partial_context_skips": 0,
730 + "routing_draws": 0
731 + }
732 + },
733 + "dual-key-gumbel-philox-keymax-cw4-alpha0.4": {
734 + "configuration": {
735 + "detection_key": "18446744073709551615",
736 + "fixture": {
737 + "dominant_bias": 0,
738 + "dominant_token": null,
739 + "logit_denominator": 16,
740 + "logit_modulus": 131,
741 + "num_tokens": 64,
742 + "prompt": [],
743 + "vocabulary_size": 128
744 + },
745 + "key": "18446744073709551615",
746 + "prf": "philox",
747 + "prf_version": "philox4x32-10-v1",
748 + "scheme": "dual_key_gumbel",
749 + "scheme_config": {
750 + "context_width": 4,
751 + "detection_alpha": 0.4,
752 + "detection_deduplicate_contexts": true,
753 + "generation_alpha": 0.4,
754 + "generation_deduplicate_contexts": "single_turn",
755 + "generation_deduplicate_contexts_max_history": 8192,
756 + "p_value_threshold": 0.01
757 + }
758 + },
759 + "detection": {
760 + "is_watermarked": true,
761 + "num_scored_tokens": 64,
762 + "p_value": 5.9713149418801004e-43,
763 + "p_value_ratio": 5.9713149418801e-41,
764 + "score": 177.98266424643373
765 + },
766 + "generation": [
767 + 7,
768 + 42,
769 + 13,
770 + 55,
771 + 103,
772 + 125,
773 + 96,
774 + 85,
775 + 119,
776 + 38,
777 + 101,
778 + 108,
779 + 72,
780 + 8,
781 + 96,
782 + 120,
783 + 116,
784 + 109,
785 + 119,
786 + 62,
787 + 26,
788 + 107,
789 + 63,
790 + 113,
791 + 102,
792 + 34,
793 + 83,
794 + 90,
795 + 121,
796 + 53,
797 + 18,
798 + 67,
799 + 20,
800 + 12,
801 + 101,
802 + 40,
803 + 86,
804 + 89,
805 + 59,
806 + 81,
807 + 24,
808 + 20,
809 + 72,
810 + 51,
811 + 22,
812 + 117,
813 + 14,
814 + 88,
815 + 116,
816 + 44,
817 + 100,
818 + 118,
819 + 75,
820 + 7,
821 + 10,
822 + 70,
823 + 91,
824 + 5,
825 + 33,
826 + 22,
827 + 4,
828 + 71,
829 + 31,
830 + 121
831 + ],
832 + "resolved": {
833 + "derived_keys": {
834 + "key_a": "11075965703501209888",
835 + "key_b": "7399018451426338514"
836 + },
837 + "detector": {
838 + "alpha": 0.4,
839 + "context_width": 4,
840 + "deduplicate_contexts": true,
841 + "key_b_prf_key": "7399018451426338514",
842 + "p_value_threshold": 0.01,
843 + "prf_key": "11075965703501209888",
844 + "type": "DualKeyGumbelWatermarkDetector"
845 + },
846 + "watermark_config": {
847 + "algorithm": "dual_key_gumbel",
848 + "allow_target_only_watermarking": false,
849 + "alpha": 0.4,
850 + "context_width": 4,
851 + "deduplicate_contexts": "single_turn",
852 + "deduplicate_contexts_max_history": 8192,
853 + "prf": "philox"
854 + }
855 + },
856 + "trace": {
857 + "dedup_skips": 0,
858 + "key_b_effective": 26,
859 + "key_b_routed": 26,
860 + "partial_context_skips": 0,
861 + "routing_draws": 64
862 + }
863 + },
864 + "dual-key-gumbel-philox-repeated-cw1": {
865 + "configuration": {
866 + "detection_key": "42",
867 + "fixture": {
868 + "dominant_bias": 52,
869 + "dominant_token": 3,
870 + "logit_denominator": 16,
871 + "logit_modulus": 131,
872 + "num_tokens": 48,
873 + "prompt": [],
874 + "vocabulary_size": 16
875 + },
876 + "key": "42",
877 + "prf": "philox",
878 + "prf_version": "philox4x32-10-v1",
879 + "scheme": "dual_key_gumbel",
880 + "scheme_config": {
881 + "context_width": 1,
882 + "detection_alpha": 0.1,
883 + "detection_deduplicate_contexts": true,
884 + "generation_alpha": 0.1,
885 + "generation_deduplicate_contexts": "single_turn",
886 + "generation_deduplicate_contexts_max_history": 8192,
887 + "p_value_threshold": 0.01
888 + }
889 + },
890 + "detection": {
891 + "is_watermarked": true,
892 + "num_scored_tokens": 13,
893 + "p_value": 0.002750266035191538,
894 + "p_value_ratio": 0.2750266035191538,
895 + "score": 23.896456918277963
896 + },
897 + "generation": [
898 + 3,
899 + 3,
900 + 6,
901 + 9,
902 + 12,
903 + 3,
904 + 3,
905 + 3,
906 + 3,
907 + 10,
908 + 13,
909 + 2,
910 + 1,
911 + 1,
912 + 3,
913 + 3,
914 + 3,
915 + 6,
916 + 2,
917 + 5,
918 + 12,
919 + 8,
920 + 4,
921 + 14,
922 + 3,
923 + 6,
924 + 2,
925 + 5,
926 + 1,
927 + 3,
928 + 3,
929 + 3,
930 + 10,
931 + 6,
932 + 2,
933 + 5,
934 + 1,
935 + 3,
936 + 3,
937 + 3,
938 + 6,
939 + 13,
940 + 9,
941 + 5,
942 + 1,
943 + 3,
944 + 3,
945 + 3
946 + ],
947 + "resolved": {
948 + "derived_keys": {
949 + "key_a": "37150756557148556",
950 + "key_b": "15826636237805488045"
951 + },
952 + "detector": {
953 + "alpha": 0.1,
954 + "context_width": 1,
955 + "deduplicate_contexts": true,
956 + "key_b_prf_key": "15826636237805488045",
957 + "p_value_threshold": 0.01,
958 + "prf_key": "37150756557148556",
959 + "type": "DualKeyGumbelWatermarkDetector"
960 + },
961 + "watermark_config": {
962 + "algorithm": "dual_key_gumbel",
963 + "allow_target_only_watermarking": false,
964 + "alpha": 0.1,
965 + "context_width": 1,
966 + "deduplicate_contexts": "single_turn",
967 + "deduplicate_contexts_max_history": 8192,
968 + "prf": "philox"
969 + }
970 + },
971 + "trace": {
972 + "dedup_skips": 35,
973 + "key_b_effective": 1,
974 + "key_b_routed": 4,
975 + "partial_context_skips": 0,
976 + "routing_draws": 48
977 + }
978 + },
979 + "dual-key-gumbel-philox-repeated-cw4-all": {
980 + "configuration": {
981 + "detection_key": "42",
982 + "fixture": {
983 + "dominant_bias": 52,
984 + "dominant_token": 3,
985 + "logit_denominator": 16,
986 + "logit_modulus": 131,
987 + "num_tokens": 48,
988 + "prompt": [],
989 + "vocabulary_size": 16
990 + },
991 + "key": "42",
992 + "prf": "philox",
993 + "prf_version": "philox4x32-10-v1",
994 + "scheme": "dual_key_gumbel",
995 + "scheme_config": {
996 + "context_width": 4,
997 + "detection_alpha": 0.1,
998 + "detection_deduplicate_contexts": true,
999 + "generation_alpha": 0.1,
1000 + "generation_deduplicate_contexts": "all",
1001 + "generation_deduplicate_contexts_max_history": 8192,
1002 + "p_value_threshold": 0.01
1003 + }
1004 + },
1005 + "detection": {
1006 + "is_watermarked": true,
1007 + "num_scored_tokens": 47,
1008 + "p_value": 8.656644652657153e-07,
1009 + "p_value_ratio": 8.656644652657152e-05,
1010 + "score": 82.86886159792212
1011 + },
1012 + "generation": [
1013 + 3,
1014 + 3,
1015 + 6,
1016 + 2,
1017 + 3,
1018 + 1,
1019 + 11,
1020 + 0,
1021 + 14,
1022 + 10,
1023 + 5,
1024 + 15,
1025 + 5,
1026 + 3,
1027 + 7,
1028 + 7,
1029 + 3,
1030 + 15,
1031 + 9,
1032 + 12,
1033 + 11,
1034 + 3,
1035 + 14,
1036 + 3,
1037 + 3,
1038 + 6,
1039 + 2,
1040 + 5,
1041 + 8,
1042 + 11,
1043 + 3,
1044 + 3,
1045 + 10,
1046 + 13,
1047 + 2,
1048 + 12,
1049 + 1,
1050 + 3,
1051 + 3,
1052 + 3,
1053 + 13,
1054 + 13,
1055 + 9,
1056 + 12,
1057 + 3,
1058 + 3,
1059 + 3,
1060 + 3
1061 + ],
1062 + "resolved": {
1063 + "derived_keys": {
1064 + "key_a": "37150756557148556",
1065 + "key_b": "15826636237805488045"
1066 + },
1067 + "detector": {
1068 + "alpha": 0.1,
1069 + "context_width": 4,
1070 + "deduplicate_contexts": true,
1071 + "key_b_prf_key": "15826636237805488045",
1072 + "p_value_threshold": 0.01,
1073 + "prf_key": "37150756557148556",
1074 + "type": "DualKeyGumbelWatermarkDetector"
1075 + },
1076 + "watermark_config": {
1077 + "algorithm": "dual_key_gumbel",
1078 + "allow_target_only_watermarking": false,
1079 + "alpha": 0.1,
1080 + "context_width": 4,
1081 + "deduplicate_contexts": "all",
1082 + "deduplicate_contexts_max_history": 8192,
1083 + "prf": "philox"
1084 + }
1085 + },
1086 + "trace": {
1087 + "dedup_skips": 5,
1088 + "key_b_effective": 4,
1089 + "key_b_routed": 4,
1090 + "partial_context_skips": 4,
1091 + "routing_draws": 48
1092 + }
1093 + },
1094 + "gumbel-philox-key0-cw1": {
1095 + "configuration": {
1096 + "detection_key": "0",
1097 + "fixture": {
1098 + "dominant_bias": 0,
1099 + "dominant_token": null,
1100 + "logit_denominator": 16,
1101 + "logit_modulus": 131,
1102 + "num_tokens": 64,
1103 + "prompt": [],
1104 + "vocabulary_size": 128
1105 + },
1106 + "key": "0",
1107 + "prf": "philox",
1108 + "prf_version": "philox4x32-10-v1",
1109 + "scheme": "gumbel",
1110 + "scheme_config": {
1111 + "context_width": 1,
1112 + "detection_alpha": 0.1,
1113 + "detection_deduplicate_contexts": true,
1114 + "generation_alpha": 0.1,
1115 + "generation_deduplicate_contexts": "single_turn",
1116 + "generation_deduplicate_contexts_max_history": 8192,
1117 + "p_value_threshold": 0.01
1118 + }
1119 + },
1120 + "detection": {
1121 + "is_watermarked": true,
1122 + "num_scored_tokens": 54,
1123 + "p_value": 4.532248480235615e-47,
1124 + "p_value_ratio": 4.532248480235615e-45,
1125 + "score": 236.2768211235414
1126 + },
1127 + "generation": [
1128 + 24,
1129 + 42,
1130 + 66,
1131 + 26,
1132 + 51,
1133 + 107,
1134 + 36,
1135 + 25,
1136 + 63,
1137 + 24,
1138 + 6,
1139 + 48,
1140 + 65,
1141 + 124,
1142 + 117,
1143 + 85,
1144 + 45,
1145 + 112,
1146 + 48,
1147 + 101,
1148 + 19,
1149 + 22,
1150 + 89,
1151 + 88,
1152 + 63,
1153 + 77,
1154 + 115,
1155 + 104,
1156 + 65,
1157 + 61,
1158 + 32,
1159 + 20,
1160 + 95,
1161 + 98,
1162 + 30,
1163 + 58,
1164 + 86,
1165 + 50,
1166 + 39,
1167 + 113,
1168 + 122,
1169 + 62,
1170 + 69,
1171 + 40,
1172 + 29,
1173 + 92,
1174 + 113,
1175 + 120,
1176 + 52,
1177 + 112,
1178 + 108,
1179 + 1,
1180 + 25,
1181 + 96,
1182 + 3,
1183 + 95,
1184 + 84,
1185 + 65,
1186 + 76,
1187 + 3,
1188 + 68,
1189 + 71,
1190 + 81,
1191 + 56
1192 + ],
1193 + "resolved": {
1194 + "derived_keys": {
1195 + "key_a": null,
1196 + "key_b": null
1197 + },
1198 + "detector": {
1199 + "alpha": null,
1200 + "context_width": 1,
1201 + "deduplicate_contexts": true,
1202 + "key_b_prf_key": null,
1203 + "p_value_threshold": 0.01,
1204 + "prf_key": "0",
1205 + "type": "GumbelWatermarkDetector"
1206 + },
1207 + "watermark_config": {
1208 + "algorithm": "gumbel",
1209 + "allow_target_only_watermarking": false,
1210 + "alpha": 0.1,
1211 + "context_width": 1,
1212 + "deduplicate_contexts": "single_turn",
1213 + "deduplicate_contexts_max_history": 8192,
1214 + "prf": "philox"
1215 + }
1216 + },
1217 + "trace": {
1218 + "dedup_skips": 10,
1219 + "key_b_effective": null,
1220 + "key_b_routed": null,
1221 + "partial_context_skips": 0,
1222 + "routing_draws": 0
1223 + }
1224 + },
1225 + "gumbel-philox-key2p32-cw5-no-dedup": {
1226 + "configuration": {
1227 + "detection_key": "4294967296",
1228 + "fixture": {
1229 + "dominant_bias": 0,
1230 + "dominant_token": null,
1231 + "logit_denominator": 16,
1232 + "logit_modulus": 131,
1233 + "num_tokens": 64,
1234 + "prompt": [],
1235 + "vocabulary_size": 128
1236 + },
1237 + "key": "4294967296",
1238 + "prf": "philox",
1239 + "prf_version": "philox4x32-10-v1",
1240 + "scheme": "gumbel",
1241 + "scheme_config": {
1242 + "context_width": 5,
1243 + "detection_alpha": 0.1,
1244 + "detection_deduplicate_contexts": false,
1245 + "generation_alpha": 0.1,
1246 + "generation_deduplicate_contexts": "none",
1247 + "generation_deduplicate_contexts_max_history": 8192,
1248 + "p_value_threshold": 0.01
1249 + }
1250 + },
1251 + "detection": {
1252 + "is_watermarked": true,
1253 + "num_scored_tokens": 64,
1254 + "p_value": 1.1027575381260964e-49,
1255 + "p_value_ratio": 1.1027575381260964e-47,
1256 + "score": 263.04926919645095
1257 + },
1258 + "generation": [
1259 + 21,
1260 + 10,
1261 + 73,
1262 + 94,
1263 + 19,
1264 + 61,
1265 + 85,
1266 + 42,
1267 + 95,
1268 + 116,
1269 + 105,
1270 + 47,
1271 + 58,
1272 + 36,
1273 + 74,
1274 + 42,
1275 + 95,
1276 + 70,
1277 + 34,
1278 + 37,
1279 + 125,
1280 + 29,
1281 + 64,
1282 + 85,
1283 + 70,
1284 + 73,
1285 + 44,
1286 + 90,
1287 + 47,
1288 + 107,
1289 + 25,
1290 + 70,
1291 + 98,
1292 + 41,
1293 + 1,
1294 + 51,
1295 + 8,
1296 + 57,
1297 + 99,
1298 + 88,
1299 + 80,
1300 + 119,
1301 + 55,
1302 + 15,
1303 + 121,
1304 + 43,
1305 + 124,
1306 + 56,
1307 + 34,
1308 + 87,
1309 + 115,
1310 + 65,
1311 + 4,
1312 + 110,
1313 + 53,
1314 + 77,
1315 + 58,
1316 + 126,
1317 + 19,
1318 + 72,
1319 + 64,
1320 + 113,
1321 + 109,
1322 + 123
1323 + ],
1324 + "resolved": {
1325 + "derived_keys": {
1326 + "key_a": null,
1327 + "key_b": null
1328 + },
1329 + "detector": {
1330 + "alpha": null,
1331 + "context_width": 5,
1332 + "deduplicate_contexts": false,
1333 + "key_b_prf_key": null,
1334 + "p_value_threshold": 0.01,
1335 + "prf_key": "4294967296",
1336 + "type": "GumbelWatermarkDetector"
1337 + },
1338 + "watermark_config": {
1339 + "algorithm": "gumbel",
1340 + "allow_target_only_watermarking": false,
1341 + "alpha": 0.1,
1342 + "context_width": 5,
1343 + "deduplicate_contexts": "none",
1344 + "deduplicate_contexts_max_history": 8192,
1345 + "prf": "philox"
1346 + }
1347 + },
1348 + "trace": {
1349 + "dedup_skips": 0,
1350 + "key_b_effective": null,
1351 + "key_b_routed": null,
1352 + "partial_context_skips": 0,
1353 + "routing_draws": 0
1354 + }
1355 + },
1356 + "gumbel-philox-key42-cw4": {
1357 + "configuration": {
1358 + "detection_key": "42",
1359 + "fixture": {
1360 + "dominant_bias": 0,
1361 + "dominant_token": null,
1362 + "logit_denominator": 16,
1363 + "logit_modulus": 131,
1364 + "num_tokens": 64,
1365 + "prompt": [],
1366 + "vocabulary_size": 128
1367 + },
1368 + "key": "42",
1369 + "prf": "philox",
1370 + "prf_version": "philox4x32-10-v1",
1371 + "scheme": "gumbel",
1372 + "scheme_config": {
1373 + "context_width": 4,
1374 + "detection_alpha": 0.1,
1375 + "detection_deduplicate_contexts": true,
1376 + "generation_alpha": 0.1,
1377 + "generation_deduplicate_contexts": "single_turn",
1378 + "generation_deduplicate_contexts_max_history": 8192,
1379 + "p_value_threshold": 0.01
1380 + }
1381 + },
1382 + "detection": {
1383 + "is_watermarked": true,
1384 + "num_scored_tokens": 64,
1385 + "p_value": 7.128196280874065e-61,
1386 + "p_value_ratio": 7.128196280874065e-59,
1387 + "score": 296.27295787856264
1388 + },
1389 + "generation": [
1390 + 46,
1391 + 24,
1392 + 45,
1393 + 80,
1394 + 14,
1395 + 26,
1396 + 22,
1397 + 124,
1398 + 21,
1399 + 45,
1400 + 16,
1401 + 30,
1402 + 5,
1403 + 89,
1404 + 0,
1405 + 10,
1406 + 27,
1407 + 70,
1408 + 80,
1409 + 62,
1410 + 1,
1411 + 11,
1412 + 117,
1413 + 53,
1414 + 10,
1415 + 45,
1416 + 94,
1417 + 37,
1418 + 43,
1419 + 22,
1420 + 78,
1421 + 49,
1422 + 119,
1423 + 9,
1424 + 22,
1425 + 118,
1426 + 82,
1427 + 75,
1428 + 32,
1429 + 24,
1430 + 84,
1431 + 27,
1432 + 122,
1433 + 66,
1434 + 78,
1435 + 64,
1436 + 124,
1437 + 17,
1438 + 91,
1439 + 12,
1440 + 51,
1441 + 15,
1442 + 75,
1443 + 64,
1444 + 127,
1445 + 45,
1446 + 112,
1447 + 15,
1448 + 83,
1449 + 114,
1450 + 121,
1451 + 109,
1452 + 24,
1453 + 77
1454 + ],
1455 + "resolved": {
1456 + "derived_keys": {
1457 + "key_a": null,
1458 + "key_b": null
1459 + },
1460 + "detector": {
1461 + "alpha": null,
1462 + "context_width": 4,
1463 + "deduplicate_contexts": true,
1464 + "key_b_prf_key": null,
1465 + "p_value_threshold": 0.01,
1466 + "prf_key": "42",
1467 + "type": "GumbelWatermarkDetector"
1468 + },
1469 + "watermark_config": {
1470 + "algorithm": "gumbel",
1471 + "allow_target_only_watermarking": false,
1472 + "alpha": 0.1,
1473 + "context_width": 4,
1474 + "deduplicate_contexts": "single_turn",
1475 + "deduplicate_contexts_max_history": 8192,
1476 + "prf": "philox"
1477 + }
1478 + },
1479 + "trace": {
1480 + "dedup_skips": 0,
1481 + "key_b_effective": null,
1482 + "key_b_routed": null,
1483 + "partial_context_skips": 0,
1484 + "routing_draws": 0
1485 + }
1486 + },
1487 + "gumbel-philox-key42-cw4-all": {
1488 + "configuration": {
1489 + "detection_key": "42",
1490 + "fixture": {
1491 + "dominant_bias": 0,
1492 + "dominant_token": null,
1493 + "logit_denominator": 16,
1494 + "logit_modulus": 131,
1495 + "num_tokens": 64,
1496 + "prompt": [],
1497 + "vocabulary_size": 128
1498 + },
1499 + "key": "42",
1500 + "prf": "philox",
1501 + "prf_version": "philox4x32-10-v1",
1502 + "scheme": "gumbel",
1503 + "scheme_config": {
1504 + "context_width": 4,
1505 + "detection_alpha": 0.1,
1506 + "detection_deduplicate_contexts": true,
1507 + "generation_alpha": 0.1,
1508 + "generation_deduplicate_contexts": "all",
1509 + "generation_deduplicate_contexts_max_history": 8192,
1510 + "p_value_threshold": 0.01
1511 + }
1512 + },
1513 + "detection": {
1514 + "is_watermarked": true,
1515 + "num_scored_tokens": 64,
1516 + "p_value": 6.0283750962334736e-55,
1517 + "p_value_ratio": 6.028375096233473e-53,
1518 + "score": 278.8159437407263
1519 + },
1520 + "generation": [
1521 + 46,
1522 + 42,
1523 + 38,
1524 + 34,
1525 + 122,
1526 + 33,
1527 + 43,
1528 + 117,
1529 + 24,
1530 + 52,
1531 + 29,
1532 + 115,
1533 + 12,
1534 + 32,
1535 + 121,
1536 + 124,
1537 + 81,
1538 + 45,
1539 + 112,
1540 + 16,
1541 + 1,
1542 + 100,
1543 + 11,
1544 + 46,
1545 + 95,
1546 + 77,
1547 + 80,
1548 + 5,
1549 + 53,
1550 + 114,
1551 + 64,
1552 + 67,
1553 + 37,
1554 + 52,
1555 + 62,
1556 + 125,
1557 + 107,
1558 + 75,
1559 + 39,
1560 + 28,
1561 + 126,
1562 + 20,
1563 + 23,
1564 + 61,
1565 + 54,
1566 + 3,
1567 + 116,
1568 + 10,
1569 + 59,
1570 + 27,
1571 + 58,
1572 + 104,
1573 + 124,
1574 + 2,
1575 + 28,
1576 + 38,
1577 + 91,
1578 + 101,
1579 + 118,
1580 + 103,
1581 + 46,
1582 + 110,
1583 + 70,
1584 + 45
1585 + ],
1586 + "resolved": {
1587 + "derived_keys": {
1588 + "key_a": null,
1589 + "key_b": null
1590 + },
1591 + "detector": {
1592 + "alpha": null,
1593 + "context_width": 4,
1594 + "deduplicate_contexts": true,
1595 + "key_b_prf_key": null,
1596 + "p_value_threshold": 0.01,
1597 + "prf_key": "42",
1598 + "type": "GumbelWatermarkDetector"
1599 + },
1600 + "watermark_config": {
1601 + "algorithm": "gumbel",
1602 + "allow_target_only_watermarking": false,
1603 + "alpha": 0.1,
1604 + "context_width": 4,
1605 + "deduplicate_contexts": "all",
1606 + "deduplicate_contexts_max_history": 8192,
1607 + "prf": "philox"
1608 + }
1609 + },
1610 + "trace": {
1611 + "dedup_skips": 4,
1612 + "key_b_effective": null,
1613 + "key_b_routed": null,
1614 + "partial_context_skips": 4,
1615 + "routing_draws": 0
1616 + }
1617 + },
1618 + "gumbel-philox-key42-cw4-all-prompt": {
1619 + "configuration": {
1620 + "detection_key": "42",
1621 + "fixture": {
1622 + "dominant_bias": 0,
1623 + "dominant_token": null,
1624 + "logit_denominator": 16,
1625 + "logit_modulus": 131,
1626 + "num_tokens": 64,
1627 + "prompt": [
1628 + 46,
1629 + 42,
1630 + 38,
1631 + 34,
1632 + 122
1633 + ],
1634 + "vocabulary_size": 128
1635 + },
1636 + "key": "42",
1637 + "prf": "philox",
1638 + "prf_version": "philox4x32-10-v1",
1639 + "scheme": "gumbel",
1640 + "scheme_config": {
1641 + "context_width": 4,
1642 + "detection_alpha": 0.1,
1643 + "detection_deduplicate_contexts": true,
1644 + "generation_alpha": 0.1,
1645 + "generation_deduplicate_contexts": "all",
1646 + "generation_deduplicate_contexts_max_history": 8192,
1647 + "p_value_threshold": 0.01
1648 + }
1649 + },
1650 + "detection": {
1651 + "is_watermarked": true,
1652 + "num_scored_tokens": 64,
1653 + "p_value": 4.330336079473992e-56,
1654 + "p_value_ratio": 4.3303360794739913e-54,
1655 + "score": 282.2076506349452
1656 + },
1657 + "generation": [
1658 + 46,
1659 + 42,
1660 + 38,
1661 + 34,
1662 + 30,
1663 + 43,
1664 + 121,
1665 + 123,
1666 + 91,
1667 + 17,
1668 + 66,
1669 + 58,
1670 + 54,
1671 + 103,
1672 + 28,
1673 + 39,
1674 + 3,
1675 + 34,
1676 + 58,
1677 + 23,
1678 + 21,
1679 + 29,
1680 + 57,
1681 + 9,
1682 + 66,
1683 + 91,
1684 + 101,
1685 + 58,
1686 + 85,
1687 + 67,
1688 + 18,
1689 + 102,
1690 + 123,
1691 + 112,
1692 + 2,
1693 + 37,
1694 + 100,
1695 + 32,
1696 + 35,
1697 + 67,
1698 + 116,
1699 + 115,
1700 + 108,
1701 + 5,
1702 + 100,
1703 + 14,
1704 + 67,
1705 + 56,
1706 + 122,
1707 + 23,
1708 + 12,
1709 + 125,
1710 + 103,
1711 + 20,
1712 + 70,
1713 + 8,
1714 + 45,
1715 + 1,
1716 + 90,
1717 + 45,
1718 + 114,
1719 + 10,
1720 + 30,
1721 + 66
1722 + ],
1723 + "resolved": {
1724 + "derived_keys": {
1725 + "key_a": null,
1726 + "key_b": null
1727 + },
1728 + "detector": {
1729 + "alpha": null,
1730 + "context_width": 4,
1731 + "deduplicate_contexts": true,
1732 + "key_b_prf_key": null,
1733 + "p_value_threshold": 0.01,
1734 + "prf_key": "42",
1735 + "type": "GumbelWatermarkDetector"
1736 + },
1737 + "watermark_config": {
1738 + "algorithm": "gumbel",
1739 + "allow_target_only_watermarking": false,
1740 + "alpha": 0.1,
1741 + "context_width": 4,
1742 + "deduplicate_contexts": "all",
1743 + "deduplicate_contexts_max_history": 8192,
1744 + "prf": "philox"
1745 + }
1746 + },
1747 + "trace": {
1748 + "dedup_skips": 5,
1749 + "key_b_effective": null,
1750 + "key_b_routed": null,
1751 + "partial_context_skips": 4,
1752 + "routing_draws": 0
1753 + }
1754 + },
1755 + "gumbel-philox-key42-cw4-mid-all": {
1756 + "configuration": {
1757 + "detection_key": "42",
1758 + "fixture": {
1759 + "dominant_bias": 16,
1760 + "dominant_token": 3,
1761 + "logit_denominator": 16,
1762 + "logit_modulus": 131,
1763 + "num_tokens": 64,
1764 + "prompt": [],
1765 + "vocabulary_size": 8
1766 + },
1767 + "key": "42",
1768 + "prf": "philox",
1769 + "prf_version": "philox4x32-10-v1",
1770 + "scheme": "gumbel",
1771 + "scheme_config": {
1772 + "context_width": 4,
1773 + "detection_alpha": 0.1,
1774 + "detection_deduplicate_contexts": true,
1775 + "generation_alpha": 0.1,
1776 + "generation_deduplicate_contexts": "all",
1777 + "generation_deduplicate_contexts_max_history": null,
1778 + "p_value_threshold": 0.01
1779 + }
1780 + },
1781 + "detection": {
1782 + "is_watermarked": true,
1783 + "num_scored_tokens": 58,
1784 + "p_value": 1.6692708568346358e-08,
1785 + "p_value_ratio": 1.6692708568346358e-06,
1786 + "score": 110.3077257312174
1787 + },
1788 + "generation": [
1789 + 7,
1790 + 3,
1791 + 6,
1792 + 2,
1793 + 5,
1794 + 1,
1795 + 7,
1796 + 3,
1797 + 6,
1798 + 6,
1799 + 5,
1800 + 2,
1801 + 5,
1802 + 4,
1803 + 3,
1804 + 0,
1805 + 3,
1806 + 6,
1807 + 5,
1808 + 5,
1809 + 1,
1810 + 7,
1811 + 4,
1812 + 3,
1813 + 3,
1814 + 6,
1815 + 1,
1816 + 5,
1817 + 1,
1818 + 3,
1819 + 3,
1820 + 3,
1821 + 6,
1822 + 6,
1823 + 2,
1824 + 3,
1825 + 1,
1826 + 4,
1827 + 7,
1828 + 3,
1829 + 6,
1830 + 2,
1831 + 5,
1832 + 5,
1833 + 0,
1834 + 0,
1835 + 7,
1836 + 3,
1837 + 6,
1838 + 2,
1839 + 5,
1840 + 1,
1841 + 4,
1842 + 2,
1843 + 3,
1844 + 3,
1845 + 2,
1846 + 5,
1847 + 4,
1848 + 1,
1849 + 4,
1850 + 7,
1851 + 3,
1852 + 6
1853 + ],
1854 + "resolved": {
1855 + "derived_keys": {
1856 + "key_a": null,
1857 + "key_b": null
1858 + },
1859 + "detector": {
1860 + "alpha": null,
1861 + "context_width": 4,
1862 + "deduplicate_contexts": true,
1863 + "key_b_prf_key": null,
1864 + "p_value_threshold": 0.01,
1865 + "prf_key": "42",
1866 + "type": "GumbelWatermarkDetector"
1867 + },
1868 + "watermark_config": {
1869 + "algorithm": "gumbel",
1870 + "allow_target_only_watermarking": false,
1871 + "alpha": 0.1,
1872 + "context_width": 4,
1873 + "deduplicate_contexts": "all",
1874 + "deduplicate_contexts_max_history": null,
1875 + "prf": "philox"
1876 + }
1877 + },
1878 + "trace": {
1879 + "dedup_skips": 10,
1880 + "key_b_effective": null,
1881 + "key_b_routed": null,
1882 + "partial_context_skips": 4,
1883 + "routing_draws": 0
1884 + }
1885 + },
1886 + "gumbel-philox-key42-cw4-mid-history-none": {
1887 + "configuration": {
1888 + "detection_key": "42",
1889 + "fixture": {
1890 + "dominant_bias": 16,
1891 + "dominant_token": 3,
1892 + "logit_denominator": 16,
1893 + "logit_modulus": 131,
1894 + "num_tokens": 64,
1895 + "prompt": [],
1896 + "vocabulary_size": 8
1897 + },
1898 + "key": "42",
1899 + "prf": "philox",
1900 + "prf_version": "philox4x32-10-v1",
1901 + "scheme": "gumbel",
1902 + "scheme_config": {
1903 + "context_width": 4,
1904 + "detection_alpha": 0.1,
1905 + "detection_deduplicate_contexts": true,
1906 + "generation_alpha": 0.1,
1907 + "generation_deduplicate_contexts": "single_turn",
1908 + "generation_deduplicate_contexts_max_history": null,
1909 + "p_value_threshold": 0.01
1910 + }
1911 + },
1912 + "detection": {
1913 + "is_watermarked": true,
1914 + "num_scored_tokens": 58,
1915 + "p_value": 1.3019898400616044e-08,
1916 + "p_value_ratio": 1.3019898400616043e-06,
1917 + "score": 110.81134114351678
1918 + },
1919 + "generation": [
1920 + 7,
1921 + 2,
1922 + 2,
1923 + 5,
1924 + 5,
1925 + 4,
1926 + 4,
1927 + 0,
1928 + 3,
1929 + 2,
1930 + 6,
1931 + 2,
1932 + 5,
1933 + 1,
1934 + 7,
1935 + 3,
1936 + 6,
1937 + 6,
1938 + 5,
1939 + 5,
1940 + 7,
1941 + 0,
1942 + 3,
1943 + 0,
1944 + 3,
1945 + 6,
1946 + 5,
1947 + 5,
1948 + 1,
1949 + 7,
1950 + 7,
1951 + 3,
1952 + 6,
1953 + 2,
1954 + 2,
1955 + 5,
1956 + 1,
1957 + 4,
1958 + 2,
1959 + 3,
1960 + 5,
1961 + 2,
1962 + 5,
1963 + 4,
1964 + 3,
1965 + 4,
1966 + 0,
1967 + 6,
1968 + 6,
1969 + 5,
1970 + 5,
1971 + 1,
1972 + 4,
1973 + 7,
1974 + 7,
1975 + 3,
1976 + 6,
1977 + 2,
1978 + 5,
1979 + 1,
1980 + 4,
1981 + 0,
1982 + 3,
1983 + 6
1984 + ],
1985 + "resolved": {
1986 + "derived_keys": {
1987 + "key_a": null,
1988 + "key_b": null
1989 + },
1990 + "detector": {
1991 + "alpha": null,
1992 + "context_width": 4,
1993 + "deduplicate_contexts": true,
1994 + "key_b_prf_key": null,
1995 + "p_value_threshold": 0.01,
1996 + "prf_key": "42",
1997 + "type": "GumbelWatermarkDetector"
1998 + },
1999 + "watermark_config": {
2000 + "algorithm": "gumbel",
2001 + "allow_target_only_watermarking": false,
2002 + "alpha": 0.1,
2003 + "context_width": 4,
2004 + "deduplicate_contexts": "single_turn",
2005 + "deduplicate_contexts_max_history": null,
2006 + "prf": "philox"
2007 + }
2008 + },
2009 + "trace": {
2010 + "dedup_skips": 6,
2011 + "key_b_effective": null,
2012 + "key_b_routed": null,
2013 + "partial_context_skips": 0,
2014 + "routing_draws": 0
2015 + }
2016 + },
2017 + "gumbel-philox-key42-cw4-mid-history8": {
2018 + "configuration": {
2019 + "detection_key": "42",
2020 + "fixture": {
2021 + "dominant_bias": 16,
2022 + "dominant_token": 3,
2023 + "logit_denominator": 16,
2024 + "logit_modulus": 131,
2025 + "num_tokens": 64,
2026 + "prompt": [],
2027 + "vocabulary_size": 8
2028 + },
2029 + "key": "42",
2030 + "prf": "philox",
2031 + "prf_version": "philox4x32-10-v1",
2032 + "scheme": "gumbel",
2033 + "scheme_config": {
2034 + "context_width": 4,
2035 + "detection_alpha": 0.1,
2036 + "detection_deduplicate_contexts": true,
2037 + "generation_alpha": 0.1,
2038 + "generation_deduplicate_contexts": "single_turn",
2039 + "generation_deduplicate_contexts_max_history": 8,
2040 + "p_value_threshold": 0.01
2041 + }
2042 + },
2043 + "detection": {
2044 + "is_watermarked": true,
2045 + "num_scored_tokens": 58,
2046 + "p_value": 2.772032243308163e-09,
2047 + "p_value_ratio": 2.772032243308163e-07,
2048 + "score": 113.89758610123921
2049 + },
2050 + "generation": [
2051 + 7,
2052 + 2,
2053 + 2,
2054 + 5,
2055 + 5,
2056 + 4,
2057 + 4,
2058 + 0,
2059 + 3,
2060 + 2,
2061 + 6,
2062 + 2,
2063 + 5,
2064 + 1,
2065 + 7,
2066 + 3,
2067 + 6,
2068 + 6,
2069 + 5,
2070 + 5,
2071 + 7,
2072 + 0,
2073 + 3,
2074 + 0,
2075 + 3,
2076 + 6,
2077 + 5,
2078 + 5,
2079 + 1,
2080 + 7,
2081 + 7,
2082 + 3,
2083 + 6,
2084 + 2,
2085 + 2,
2086 + 5,
2087 + 1,
2088 + 4,
2089 + 2,
2090 + 3,
2091 + 5,
2092 + 2,
2093 + 5,
2094 + 4,
2095 + 3,
2096 + 4,
2097 + 0,
2098 + 6,
2099 + 6,
2100 + 5,
2101 + 5,
2102 + 7,
2103 + 0,
2104 + 3,
2105 + 7,
2106 + 3,
2107 + 2,
2108 + 2,
2109 + 5,
2110 + 4,
2111 + 4,
2112 + 0,
2113 + 3,
2114 + 2
2115 + ],
2116 + "resolved": {
2117 + "derived_keys": {
2118 + "key_a": null,
2119 + "key_b": null
2120 + },
2121 + "detector": {
2122 + "alpha": null,
2123 + "context_width": 4,
2124 + "deduplicate_contexts": true,
2125 + "key_b_prf_key": null,
2126 + "p_value_threshold": 0.01,
2127 + "prf_key": "42",
2128 + "type": "GumbelWatermarkDetector"
2129 + },
2130 + "watermark_config": {
2131 + "algorithm": "gumbel",
2132 + "allow_target_only_watermarking": false,
2133 + "alpha": 0.1,
2134 + "context_width": 4,
2135 + "deduplicate_contexts": "single_turn",
2136 + "deduplicate_contexts_max_history": 8,
2137 + "prf": "philox"
2138 + }
2139 + },
2140 + "trace": {
2141 + "dedup_skips": 0,
2142 + "key_b_effective": null,
2143 + "key_b_routed": null,
2144 + "partial_context_skips": 0,
2145 + "routing_draws": 0
2146 + }
2147 + },
2148 + "gumbel-philox-key42-cw4-mid-no-dedup": {
2149 + "configuration": {
2150 + "detection_key": "42",
2151 + "fixture": {
2152 + "dominant_bias": 16,
2153 + "dominant_token": 3,
2154 + "logit_denominator": 16,
2155 + "logit_modulus": 131,
2156 + "num_tokens": 64,
2157 + "prompt": [],
2158 + "vocabulary_size": 8
2159 + },
2160 + "key": "42",
2161 + "prf": "philox",
2162 + "prf_version": "philox4x32-10-v1",
2163 + "scheme": "gumbel",
2164 + "scheme_config": {
2165 + "context_width": 4,
2166 + "detection_alpha": 0.1,
2167 + "detection_deduplicate_contexts": false,
2168 + "generation_alpha": 0.1,
2169 + "generation_deduplicate_contexts": "none",
2170 + "generation_deduplicate_contexts_max_history": null,
2171 + "p_value_threshold": 0.01
2172 + }
2173 + },
2174 + "detection": {
2175 + "is_watermarked": true,
2176 + "num_scored_tokens": 64,
2177 + "p_value": 2.136704912577492e-10,
2178 + "p_value_ratio": 2.136704912577492e-08,
2179 + "score": 127.21876922102206
2180 + },
2181 + "generation": [
2182 + 7,
2183 + 2,
2184 + 2,
2185 + 5,
2186 + 5,
2187 + 4,
2188 + 4,
2189 + 0,
2190 + 3,
2191 + 2,
2192 + 6,
2193 + 2,
2194 + 5,
2195 + 1,
2196 + 7,
2197 + 3,
2198 + 6,
2199 + 6,
2200 + 5,
2201 + 5,
2202 + 7,
2203 + 0,
2204 + 3,
2205 + 0,
2206 + 3,
2207 + 6,
2208 + 5,
2209 + 5,
2210 + 1,
2211 + 7,
2212 + 7,
2213 + 3,
2214 + 6,
2215 + 2,
2216 + 2,
2217 + 5,
2218 + 1,
2219 + 4,
2220 + 2,
2221 + 3,
2222 + 5,
2223 + 2,
2224 + 5,
2225 + 4,
2226 + 3,
2227 + 4,
2228 + 0,
2229 + 6,
2230 + 6,
2231 + 5,
2232 + 5,
2233 + 7,
2234 + 0,
2235 + 3,
2236 + 7,
2237 + 3,
2238 + 2,
2239 + 2,
2240 + 5,
2241 + 4,
2242 + 4,
2243 + 0,
2244 + 3,
2245 + 2
2246 + ],
2247 + "resolved": {
2248 + "derived_keys": {
2249 + "key_a": null,
2250 + "key_b": null
2251 + },
2252 + "detector": {
2253 + "alpha": null,
2254 + "context_width": 4,
2255 + "deduplicate_contexts": false,
2256 + "key_b_prf_key": null,
2257 + "p_value_threshold": 0.01,
2258 + "prf_key": "42",
2259 + "type": "GumbelWatermarkDetector"
2260 + },
2261 + "watermark_config": {
2262 + "algorithm": "gumbel",
2263 + "allow_target_only_watermarking": false,
2264 + "alpha": 0.1,
2265 + "context_width": 4,
2266 + "deduplicate_contexts": "none",
2267 + "deduplicate_contexts_max_history": null,
2268 + "prf": "philox"
2269 + }
2270 + },
2271 + "trace": {
2272 + "dedup_skips": 0,
2273 + "key_b_effective": null,
2274 + "key_b_routed": null,
2275 + "partial_context_skips": 0,
2276 + "routing_draws": 0
2277 + }
2278 + },
2279 + "gumbel-philox-key42-cw4-near-threshold": {
2280 + "configuration": {
2281 + "detection_key": "42",
2282 + "fixture": {
2283 + "dominant_bias": 0,
2284 + "dominant_token": null,
2285 + "logit_denominator": 16,
2286 + "logit_modulus": 131,
2287 + "num_tokens": 8,
2288 + "prompt": [],
2289 + "vocabulary_size": 16
2290 + },
2291 + "key": "42",
2292 + "prf": "philox",
2293 + "prf_version": "philox4x32-10-v1",
2294 + "scheme": "gumbel",
2295 + "scheme_config": {
2296 + "context_width": 4,
2297 + "detection_alpha": 0.1,
2298 + "detection_deduplicate_contexts": true,
2299 + "generation_alpha": 0.1,
2300 + "generation_deduplicate_contexts": "single_turn",
2301 + "generation_deduplicate_contexts_max_history": 8192,
2302 + "p_value_threshold": 0.0015
2303 + }
2304 + },
2305 + "detection": {
2306 + "is_watermarked": true,
2307 + "num_scored_tokens": 8,
2308 + "p_value": 0.0009697880157637389,
2309 + "p_value_ratio": 0.6465253438424926,
2310 + "score": 19.672172609864035
2311 + },
2312 + "generation": [
2313 + 7,
2314 + 2,
2315 + 13,
2316 + 2,
2317 + 5,
2318 + 1,
2319 + 11,
2320 + 14
2321 + ],
2322 + "resolved": {
2323 + "derived_keys": {
2324 + "key_a": null,
2325 + "key_b": null
2326 + },
2327 + "detector": {
2328 + "alpha": null,
2329 + "context_width": 4,
2330 + "deduplicate_contexts": true,
2331 + "key_b_prf_key": null,
2332 + "p_value_threshold": 0.0015,
2333 + "prf_key": "42",
2334 + "type": "GumbelWatermarkDetector"
2335 + },
2336 + "watermark_config": {
2337 + "algorithm": "gumbel",
2338 + "allow_target_only_watermarking": false,
2339 + "alpha": 0.1,
2340 + "context_width": 4,
2341 + "deduplicate_contexts": "single_turn",
2342 + "deduplicate_contexts_max_history": 8192,
2343 + "prf": "philox"
2344 + }
2345 + },
2346 + "trace": {
2347 + "dedup_skips": 0,
2348 + "key_b_effective": null,
2349 + "key_b_routed": null,
2350 + "partial_context_skips": 0,
2351 + "routing_draws": 0
2352 + }
2353 + },
2354 + "gumbel-philox-key42-cw4-wrong-key": {
2355 + "configuration": {
2356 + "detection_key": "43",
2357 + "fixture": {
2358 + "dominant_bias": 0,
2359 + "dominant_token": null,
2360 + "logit_denominator": 16,
2361 + "logit_modulus": 131,
2362 + "num_tokens": 64,
2363 + "prompt": [],
2364 + "vocabulary_size": 128
2365 + },
2366 + "key": "42",
2367 + "prf": "philox",
2368 + "prf_version": "philox4x32-10-v1",
2369 + "scheme": "gumbel",
2370 + "scheme_config": {
2371 + "context_width": 4,
2372 + "detection_alpha": 0.1,
2373 + "detection_deduplicate_contexts": true,
2374 + "generation_alpha": 0.1,
2375 + "generation_deduplicate_contexts": "single_turn",
2376 + "generation_deduplicate_contexts_max_history": 8192,
2377 + "p_value_threshold": 0.01
2378 + }
2379 + },
2380 + "detection": {
2381 + "is_watermarked": false,
2382 + "num_scored_tokens": 64,
2383 + "p_value": 0.5208447936891313,
2384 + "p_value_ratio": 52.08447936891313,
2385 + "score": 63.25096716585621
2386 + },
2387 + "generation": [
2388 + 46,
2389 + 24,
2390 + 45,
2391 + 80,
2392 + 14,
2393 + 26,
2394 + 22,
2395 + 124,
2396 + 21,
2397 + 45,
2398 + 16,
2399 + 30,
2400 + 5,
2401 + 89,
2402 + 0,
2403 + 10,
2404 + 27,
2405 + 70,
2406 + 80,
2407 + 62,
2408 + 1,
2409 + 11,
2410 + 117,
2411 + 53,
2412 + 10,
2413 + 45,
2414 + 94,
2415 + 37,
2416 + 43,
2417 + 22,
2418 + 78,
2419 + 49,
2420 + 119,
2421 + 9,
2422 + 22,
2423 + 118,
2424 + 82,
2425 + 75,
2426 + 32,
2427 + 24,
2428 + 84,
2429 + 27,
2430 + 122,
2431 + 66,
2432 + 78,
2433 + 64,
2434 + 124,
2435 + 17,
2436 + 91,
2437 + 12,
2438 + 51,
2439 + 15,
2440 + 75,
2441 + 64,
2442 + 127,
2443 + 45,
2444 + 112,
2445 + 15,
2446 + 83,
2447 + 114,
2448 + 121,
2449 + 109,
2450 + 24,
2451 + 77
2452 + ],
2453 + "resolved": {
2454 + "derived_keys": {
2455 + "key_a": null,
2456 + "key_b": null
2457 + },
2458 + "detector": {
2459 + "alpha": null,
2460 + "context_width": 4,
2461 + "deduplicate_contexts": true,
2462 + "key_b_prf_key": null,
2463 + "p_value_threshold": 0.01,
2464 + "prf_key": "43",
2465 + "type": "GumbelWatermarkDetector"
2466 + },
2467 + "watermark_config": {
2468 + "algorithm": "gumbel",
2469 + "allow_target_only_watermarking": false,
2470 + "alpha": 0.1,
2471 + "context_width": 4,
2472 + "deduplicate_contexts": "single_turn",
2473 + "deduplicate_contexts_max_history": 8192,
2474 + "prf": "philox"
2475 + }
2476 + },
2477 + "trace": {
2478 + "dedup_skips": 0,
2479 + "key_b_effective": null,
2480 + "key_b_routed": null,
2481 + "partial_context_skips": 0,
2482 + "routing_draws": 0
2483 + }
2484 + },
2485 + "gumbel-philox-keymax-cw16-all": {
2486 + "configuration": {
2487 + "detection_key": "18446744073709551615",
2488 + "fixture": {
2489 + "dominant_bias": 0,
2490 + "dominant_token": null,
2491 + "logit_denominator": 16,
2492 + "logit_modulus": 131,
2493 + "num_tokens": 64,
2494 + "prompt": [],
2495 + "vocabulary_size": 128
2496 + },
2497 + "key": "18446744073709551615",
2498 + "prf": "philox",
2499 + "prf_version": "philox4x32-10-v1",
2500 + "scheme": "gumbel",
2501 + "scheme_config": {
2502 + "context_width": 16,
2503 + "detection_alpha": 0.1,
2504 + "detection_deduplicate_contexts": true,
2505 + "generation_alpha": 0.1,
2506 + "generation_deduplicate_contexts": "all",
2507 + "generation_deduplicate_contexts_max_history": 8192,
2508 + "p_value_threshold": 0.01
2509 + }
2510 + },
2511 + "detection": {
2512 + "is_watermarked": true,
2513 + "num_scored_tokens": 64,
2514 + "p_value": 6.166345567165398e-36,
2515 + "p_value_ratio": 6.1663455671653984e-34,
2516 + "score": 220.2767573421712
2517 + },
2518 + "generation": [
2519 + 46,
2520 + 42,
2521 + 38,
2522 + 34,
2523 + 30,
2524 + 26,
2525 + 22,
2526 + 18,
2527 + 14,
2528 + 10,
2529 + 6,
2530 + 2,
2531 + 44,
2532 + 125,
2533 + 121,
2534 + 117,
2535 + 88,
2536 + 6,
2537 + 97,
2538 + 12,
2539 + 86,
2540 + 121,
2541 + 0,
2542 + 106,
2543 + 98,
2544 + 119,
2545 + 101,
2546 + 36,
2547 + 47,
2548 + 110,
2549 + 25,
2550 + 24,
2551 + 102,
2552 + 6,
2553 + 101,
2554 + 37,
2555 + 114,
2556 + 50,
2557 + 53,
2558 + 127,
2559 + 109,
2560 + 80,
2561 + 69,
2562 + 5,
2563 + 96,
2564 + 60,
2565 + 14,
2566 + 42,
2567 + 38,
2568 + 58,
2569 + 118,
2570 + 79,
2571 + 4,
2572 + 110,
2573 + 42,
2574 + 102,
2575 + 66,
2576 + 80,
2577 + 72,
2578 + 33,
2579 + 78,
2580 + 106,
2581 + 67,
2582 + 52
2583 + ],
2584 + "resolved": {
2585 + "derived_keys": {
2586 + "key_a": null,
2587 + "key_b": null
2588 + },
2589 + "detector": {
2590 + "alpha": null,
2591 + "context_width": 16,
2592 + "deduplicate_contexts": true,
2593 + "key_b_prf_key": null,
2594 + "p_value_threshold": 0.01,
2595 + "prf_key": "18446744073709551615",
2596 + "type": "GumbelWatermarkDetector"
2597 + },
2598 + "watermark_config": {
2599 + "algorithm": "gumbel",
2600 + "allow_target_only_watermarking": false,
2601 + "alpha": 0.1,
2602 + "context_width": 16,
2603 + "deduplicate_contexts": "all",
2604 + "deduplicate_contexts_max_history": 8192,
2605 + "prf": "philox"
2606 + }
2607 + },
2608 + "trace": {
2609 + "dedup_skips": 16,
2610 + "key_b_effective": null,
2611 + "key_b_routed": null,
2612 + "partial_context_skips": 16,
2613 + "routing_draws": 0
2614 + }
2615 + },
2616 + "gumbel-philox-repeated-cw1": {
2617 + "configuration": {
2618 + "detection_key": "42",
2619 + "fixture": {
2620 + "dominant_bias": 52,
2621 + "dominant_token": 3,
2622 + "logit_denominator": 16,
2623 + "logit_modulus": 131,
2624 + "num_tokens": 48,
2625 + "prompt": [],
2626 + "vocabulary_size": 16
2627 + },
2628 + "key": "42",
2629 + "prf": "philox",
2630 + "prf_version": "philox4x32-10-v1",
2631 + "scheme": "gumbel",
2632 + "scheme_config": {
2633 + "context_width": 1,
2634 + "detection_alpha": 0.1,
2635 + "detection_deduplicate_contexts": true,
2636 + "generation_alpha": 0.1,
2637 + "generation_deduplicate_contexts": "single_turn",
2638 + "generation_deduplicate_contexts_max_history": 8192,
2639 + "p_value_threshold": 0.01
2640 + }
2641 + },
2642 + "detection": {
2643 + "is_watermarked": true,
2644 + "num_scored_tokens": 15,
2645 + "p_value": 6.97501804959627e-05,
2646 + "p_value_ratio": 0.00697501804959627,
2647 + "score": 34.40948699058768
2648 + },
2649 + "generation": [
2650 + 14,
2651 + 3,
2652 + 13,
2653 + 2,
2654 + 1,
2655 + 8,
2656 + 3,
2657 + 3,
2658 + 3,
2659 + 10,
2660 + 2,
2661 + 2,
2662 + 5,
2663 + 15,
2664 + 11,
2665 + 3,
2666 + 3,
2667 + 6,
2668 + 2,
2669 + 5,
2670 + 12,
2671 + 11,
2672 + 3,
2673 + 3,
2674 + 3,
2675 + 6,
2676 + 2,
2677 + 5,
2678 + 1,
2679 + 3,
2680 + 3,
2681 + 3,
2682 + 10,
2683 + 6,
2684 + 2,
2685 + 5,
2686 + 1,
2687 + 3,
2688 + 3,
2689 + 3,
2690 + 6,
2691 + 13,
2692 + 9,
2693 + 4,
2694 + 11,
2695 + 3,
2696 + 3,
2697 + 3
2698 + ],
2699 + "resolved": {
2700 + "derived_keys": {
2701 + "key_a": null,
2702 + "key_b": null
2703 + },
2704 + "detector": {
2705 + "alpha": null,
2706 + "context_width": 1,
2707 + "deduplicate_contexts": true,
2708 + "key_b_prf_key": null,
2709 + "p_value_threshold": 0.01,
2710 + "prf_key": "42",
2711 + "type": "GumbelWatermarkDetector"
2712 + },
2713 + "watermark_config": {
2714 + "algorithm": "gumbel",
2715 + "allow_target_only_watermarking": false,
2716 + "alpha": 0.1,
2717 + "context_width": 1,
2718 + "deduplicate_contexts": "single_turn",
2719 + "deduplicate_contexts_max_history": 8192,
2720 + "prf": "philox"
2721 + }
2722 + },
2723 + "trace": {
2724 + "dedup_skips": 33,
2725 + "key_b_effective": null,
2726 + "key_b_routed": null,
2727 + "partial_context_skips": 0,
2728 + "routing_draws": 0
2729 + }
2730 + },
2731 + "gumbel-philox-repeated-cw4-all": {
2732 + "configuration": {
2733 + "detection_key": "42",
2734 + "fixture": {
2735 + "dominant_bias": 52,
2736 + "dominant_token": 3,
2737 + "logit_denominator": 16,
2738 + "logit_modulus": 131,
2739 + "num_tokens": 48,
2740 + "prompt": [],
2741 + "vocabulary_size": 16
2742 + },
2743 + "key": "42",
2744 + "prf": "philox",
2745 + "prf_version": "philox4x32-10-v1",
2746 + "scheme": "gumbel",
2747 + "scheme_config": {
2748 + "context_width": 4,
2749 + "detection_alpha": 0.1,
2750 + "detection_deduplicate_contexts": true,
2751 + "generation_alpha": 0.1,
2752 + "generation_deduplicate_contexts": "all",
2753 + "generation_deduplicate_contexts_max_history": 8192,
2754 + "p_value_threshold": 0.01
2755 + }
2756 + },
2757 + "detection": {
2758 + "is_watermarked": true,
2759 + "num_scored_tokens": 47,
2760 + "p_value": 6.687298332875021e-08,
2761 + "p_value_ratio": 6.687298332875021e-06,
2762 + "score": 92.47607252164309
2763 + },
2764 + "generation": [
2765 + 3,
2766 + 3,
2767 + 6,
2768 + 2,
2769 + 4,
2770 + 1,
2771 + 3,
2772 + 3,
2773 + 3,
2774 + 10,
2775 + 2,
2776 + 2,
2777 + 3,
2778 + 8,
2779 + 4,
2780 + 3,
2781 + 3,
2782 + 6,
2783 + 12,
2784 + 5,
2785 + 8,
2786 + 4,
2787 + 11,
2788 + 10,
2789 + 3,
2790 + 6,
2791 + 9,
2792 + 5,
2793 + 8,
2794 + 11,
2795 + 3,
2796 + 3,
2797 + 10,
2798 + 6,
2799 + 1,
2800 + 12,
2801 + 3,
2802 + 3,
2803 + 3,
2804 + 3,
2805 + 6,
2806 + 9,
2807 + 5,
2808 + 5,
2809 + 8,
2810 + 0,
2811 + 0,
2812 + 3
2813 + ],
2814 + "resolved": {
2815 + "derived_keys": {
2816 + "key_a": null,
2817 + "key_b": null
2818 + },
2819 + "detector": {
2820 + "alpha": null,
2821 + "context_width": 4,
2822 + "deduplicate_contexts": true,
2823 + "key_b_prf_key": null,
2824 + "p_value_threshold": 0.01,
2825 + "prf_key": "42",
2826 + "type": "GumbelWatermarkDetector"
2827 + },
2828 + "watermark_config": {
2829 + "algorithm": "gumbel",
2830 + "allow_target_only_watermarking": false,
2831 + "alpha": 0.1,
2832 + "context_width": 4,
2833 + "deduplicate_contexts": "all",
2834 + "deduplicate_contexts_max_history": 8192,
2835 + "prf": "philox"
2836 + }
2837 + },
2838 + "trace": {
2839 + "dedup_skips": 5,
2840 + "key_b_effective": null,
2841 + "key_b_routed": null,
2842 + "partial_context_skips": 4,
2843 + "routing_draws": 0
2844 + }
2845 + }
2846 + },
2847 + "schema_version": 3
2848 +}