"""MaAI ch_kyoto standard/noise-robust Mimi VAP: causal stereo CPU replay.""" import argparse from collections import Counter import contextlib from datetime import datetime, timezone import importlib.metadata as metadata import io import json import platform from pathlib import Path import sys import time from smoke_run import HERE, ROOT, Maai, MaaiInput, np, sf, soxr, torch from evaluate_can_reply import metrics, read, write from run_user_only import LABELS, HISTORY, seed, sha OUT = HERE / "results/mimi-standard" MODELS = HERE / "models/mimi" VAP = MODELS / "vap_mimi_state_dict_ch_kyoto_12.5hz_20000msec.pt" MC = False ONNX = MODELS / "continuous_mimi_fp32.onnx" META = MODELS / "continuous_mimi_fp32.json" MANIFEST = HERE / "mimi-model-manifest.json" FRAME_MS, FRAME_SAMPLES = 80, 1280 def validate_weights(): manifest = json.loads(MANIFEST.read_text()) assert {x["filename"] for x in manifest["files"]} == { "vap_mimi_state_dict_ch_kyoto_12.5hz_20000msec.pt", "vap_mc_mimi_state_dict_ch_kyoto_12.5hz_20000msec.pt", ONNX.name, META.name} for item in manifest["files"]: path = MODELS / item["filename"] assert sha(path) == item["sha256"] and path.stat().st_size == item["size"], path.name return manifest def model(): seed() messages = io.StringIO() with contextlib.redirect_stdout(messages): maai = Maai(mode="vap", lang="ch_kyoto", mc=MC, audio_ch1=MaaiInput.Chunk(), audio_ch2=MaaiInput.Chunk(), model_type="normal-ver2", frame_rate=12.5, context_len_sec=20, device="cpu", use_kv_cache=True, local_model=str(VAP), use_mimi_onnx=True, mimi_onnx_precision="fp32", mimi_local_onnx_fp32_path=str(ONNX), mimi_local_onnx_fp32_meta_path=str(META), mimi_onnx_cpu_intra_threads=4, mimi_onnx_cpu_inter_threads=1, cache_dir=str(HERE / ".cache/huggingface")) # Official local_model path uses the common 'vap' architecture marker for # either checkpoint. The filename/hash and loading audit identify MC weights. assert maai.variant == "vap" and not maai.is_mono assert maai.encoder_type == "mimi" and not maai.single_tower assert maai.audio_samples_per_frame == FRAME_SAMPLES maai.CALC_PROCESS_TIME_INTERVAL = 10**9 return maai, messages.getvalue() def loading_audit(maai, messages): raw = torch.load(VAP, map_location="cpu", weights_only=True) sd = {**raw.get("encoder_state_dict", {}), **raw["state_dict"]} if "state_dict" in raw else raw missing, mismatch, bypassed = [], [], [] for name, parameter in maai.vap.named_parameters(): key = name if name.startswith(("encoder1.", "encoder2.")): encoder = getattr(maai.vap, name.split('.', 1)[0]) # At native 12.5Hz, _align_frame_rate returns embeddings directly; # these frozen conversion convolutions are not used or pretrained. if '.frame_rate_conv.' in name and not parameter.requires_grad: assert encoder.frame_hz == encoder.frame_hz_mimi == 12.5 bypassed.append(name) continue key = "encoder." + name.split(".", 1)[1] if key not in sd: missing.append(name) elif not torch.equal(parameter.detach().cpu(), sd[key]): mismatch.append(name) assert not missing and not mismatch, dict(missing=missing, mismatched=mismatch) return dict(variant="vap_mc" if MC else "vap", loader_architecture_marker=maai.variant, encoder=maai.encoder_type, checkpoint_mc=MC, single_tower=maai.single_tower, missing_trainable_parameters=missing, mismatched_parameters=mismatch, unused_frozen_frame_rate_parameters=bypassed, loader_messages=messages, encoder_provider=maai.vap.encoder1._onnx_sess.get_providers()) def prepare(pcm, sr, decision): duration = min(20000, decision // FRAME_MS * FRAME_MS) if duration < FRAME_MS * 2: raise ValueError("Insufficient history for Mimi startup") origin = decision - duration start, end = origin * sr // 1000, decision * sr // 1000 if not 0 <= start < end <= len(pcm): raise ValueError("Point outside recording") # Preserve BOTH recorded channels. Crop before resampling to avoid future input. segment = np.ascontiguousarray(soxr.resample(pcm[start:end], sr, 16000, quality="HQ"), dtype=np.float32) assert segment.shape == (duration // FRAME_MS * FRAME_SAMPLES, 2) return segment, origin def consume(maai, frame): maai.process(np.ascontiguousarray(frame[:, 0]), np.ascontiguousarray(frame[:, 1])) if maai.result_dict_queue.empty(): return None # Official ONNX Mimi skips its first encoder output. result = maai.result_dict_queue.get_nowait() assert maai.result_dict_queue.empty() values = {k: np.asarray(result[k], dtype=float).reshape(-1) for k in ("p_now", "p_future", "vad")} for value in values.values(): assert value.shape == (2,) and np.isfinite(value).all() assert ((value >= 0) & (value <= 1)).all() return values def score(values): return 1.0 - float(values["p_now"][0]) def replay(segment): began = time.perf_counter() maai, _ = model() load_ms = (time.perf_counter() - began) * 1000 times, values, first_output_ms = [], None, None for start in range(0, len(segment), FRAME_SAMPLES): t = time.perf_counter() values = consume(maai, segment[start:start+FRAME_SAMPLES]) times.append((time.perf_counter()-t)*1000) if values is not None and first_output_ms is None: first_output_ms = (start+FRAME_SAMPLES)*1000/16000 if values is None: raise ValueError("No score at the exact final input frame") return values, dict(model_load_ms=load_ms, replay_inference_ms=sum(times), mean_update_ms=float(np.mean(times)), p95_update_ms=float(np.percentile(times, 95)), first_output_ms=first_output_ms, frame_count=len(times)) def witness(pcm, sr, decision): maai, messages = model() audit = loading_audit(maai, messages) del maai segment, _ = prepare(pcm, sr, decision) changed = pcm.copy() changed[decision*sr//1000:] = np.random.default_rng(42).normal(0, .1, changed[decision*sr//1000:].shape) other, _ = prepare(changed, sr, decision) assert np.array_equal(segment, other), "Future audio changed a causal input" a, timing = replay(segment) b, _ = replay(other) assert all(np.array_equal(a[k], b[k]) for k in a), "Seeded inference differs" assert np.count_nonzero(segment[:, 0]) and np.count_nonzero(segment[:, 1]), "Smoke must exercise both speakers" print(f"WITNESS Mimi {'mc' if MC else 'standard'} CPU seed=42 score={score(a):.7f}; exact repeat, future invariance, both speakers and loading audit passed", flush=True) return dict(repeat_identical=True, future_invariance=True, both_channels_nonzero=True, score=score(a), timing=timing, loading_audit=audit) def continuous(pcm, sr): maai, _ = model() resampler = soxr.ResampleStream(sr, 16000, 2, dtype="float32", quality="HQ") pending = np.empty((0, 2), dtype=np.float32) points = [] block_samples = sr * FRAME_MS // 1000 assert block_samples * 1000 == sr * FRAME_MS for start in range(0, len(pcm), block_samples): end = min(len(pcm), start+block_samples) pending = np.concatenate([pending, resampler.resample_chunk(pcm[start:end], last=end == len(pcm))]) latest = None while len(pending) >= FRAME_SAMPLES: frame, pending = pending[:FRAME_SAMPLES], pending[FRAME_SAMPLES:] result = consume(maai, frame) if result is not None: latest = score(result) if latest is not None: points.append([round(end*1000/sr, 3), round(latest, 7)]) return points def provenance(manifest): import maai.encoder, maai.model return dict(machine=platform.platform(), hardware="Apple M4 Mac mini / 16 GB", python=sys.version, environment="experiments/maai-ch-kyoto/.venv (uv.lock)", device="cpu", torch_threads=4, torch_interop_threads=1, onnx_intra_threads=4, onnx_inter_threads=1, dependencies={p: metadata.version(p) for p in ("maai", "torch", "transformers", "onnxruntime", "numpy", "soundfile", "soxr")}, model_type="normal-ver2", variant="vap_mc" if MC else "vap", mc=MC, checkpoint_filename=VAP.name, encoder="mimi", encoder_precision="fp32", channels={"0": "recorded user", "1": "recorded robot"}, sample_rate=16000, frame_rate=12.5, frame_ms=FRAME_MS, context_ms=20000, seed=42, use_kv_cache=True, weights=manifest, label_sha256=sha(LABELS), history_sha256=sha(HISTORY), uv_lock_sha256=sha(HERE/"uv.lock"), script_sha256=sha(HERE/"run_mimi.py"), maai_encoder_source_sha256=sha(Path(maai.encoder.__file__)), maai_model_source_sha256=sha(Path(maai.model.__file__))) def references(prov): directory = HERE/"results/user-only" summary = json.loads((directory/"summary.json").read_text()) source = summary["provenance"] for key in ("label_sha256", "history_sha256", "uv_lock_sha256"): assert source[key] == prov[key], f"Stale CPC reference: {key}; rerun run_user_only.py all" return {(r["call_id"], int(r["decision_ms"])): r for r in read(directory/"predictions.csv")}, source, sha(directory/"predictions.csv") def main(mode, variant): global MC, VAP, OUT MC = variant == 'mc' VAP = MODELS / f"vap_{'mc_' if MC else ''}mimi_state_dict_ch_kyoto_12.5hz_20000msec.pt" OUT = HERE / ('results/mimi-mc' if MC else 'results/mimi-standard') manifest = validate_weights() prov = provenance(manifest) labels = read(LABELS) annotated = [r for r in labels if r["can_reply_now"] in ("0", "1")] assert annotated and len({(r['call_id'],r['decision_ms']) for r in labels}) == len(labels) audio_cache, audio_hashes = {}, {} for call in sorted({r["call_id"] for r in labels}): path = ROOT/"data/韩泰机器人抢话/音频"/f"{call}.mp3" pcm, sr = sf.read(path, dtype="float32", always_2d=True) assert pcm.shape[1] == 2 audio_cache[call] = pcm, sr audio_hashes[call] = sha(path) began = time.perf_counter() first = annotated[0] checks = witness(*audio_cache[first["call_id"]], int(first["decision_ms"])) if mode == "smoke": print(json.dumps(dict(witness=checks, provenance=prov), ensure_ascii=False, indent=2)) return baseline, source_prov, baseline_hash = references(prov) assert source_prov["audio_sha256"] == audio_hashes, "CPC reference uses different source recordings" OUT.mkdir(parents=True, exist_ok=True) rows = [] for index, label in enumerate(annotated): call, decision = label['call_id'], int(label['decision_ms']) segment, origin = prepare(*audio_cache[call], decision) values, timing = replay(segment) value = score(values) other = baseline[(call, decision)] assert other['can_reply_now'] == label['can_reply_now'] row = dict(label) row.update(can_reply_score=value, prediction_05=int(value > .5), correct_05=int(int(value > .5) == int(label['can_reply_now'])), p_now_user=float(values['p_now'][0]), p_now_robot=float(values['p_now'][1]), p_future_user=float(values['p_future'][0]), p_future_robot=float(values['p_future'][1]), vad_user=float(values['vad'][0]), vad_robot=float(values['vad'][1]), cpc_mc_stereo_score=float(other['stereo_can_reply_score']), cpc_mc_user_only_score=float(other['can_reply_score']), audio_start_ms=origin, audio_end_ms=decision, context_ms=decision-origin, **timing) rows.append(row) print(f"{index+1}/{len(annotated)} {call[:8]} {decision}: label={label['can_reply_now']}, Mimi={value:.4f}, CPC-mc={float(other['stereo_can_reply_score']):.4f}", flush=True) thresholds = [dict(threshold=t, **metrics(rows, 'can_reply_score', t)) for t in (.11, .5, .7, .8, .9, .95, .98)] summary = dict(model=f"VAP / MaAI Mimi {variant} (both speakers)", created_at=datetime.now(timezone.utc).isoformat(), total_points=len(rows), main_rule="1 - p_now[user] > 0.5", main_metrics=metrics(rows,'can_reply_score'), cpc_mc_stereo_reference=metrics(rows,'cpc_mc_stereo_score'), cpc_mc_user_only_reference=metrics(rows,'cpc_mc_user_only_score'), thresholds=thresholds, per_call={c:metrics([r for r in rows if r['call_id']==c],'can_reply_score') for c in audio_cache}, changed_vs_cpc_mc_stereo=dict(fixed=sum((r['cpc_mc_stereo_score']>.5)!=bool(int(r['can_reply_now'])) and bool(r['correct_05']) for r in rows), regressed=sum((r['cpc_mc_stereo_score']>.5)==bool(int(r['can_reply_now'])) and not r['correct_05'] for r in rows)), annotation_sources=dict(Counter(r['annotator'] or 'unspecified' for r in rows)), provenance={**prov, 'audio_sha256':audio_hashes, 'cpc_reference_predictions_sha256':baseline_hash}, witness=checks, method=f"{variant} (mc={MC}) ch_kyoto VAP with official FP32 ONNX Mimi encoder. Recorded user+robot stereo; crop preceding <=20s before 16kHz resampling; 80ms native frames, final frame ends at exact decision. Reset Mimi and VAP state per point, retain official skipped startup encoder output. No future input, annotation text, external end signal, silence gate or forced timeout. Score=1-p_now[user], not calibrated EOT probability.", limitations=["Comparison changes encoder/checkpoints and native framing; no held-out benchmark or claim that differences are caused by the encoder alone.", "Both models use <=20s histories but native 80ms/100ms framing may differ at recording start by less than one frame.", "Continuous full-call states differ from independent point replay; checkpoint scores need not lie on the continuous curve.", "Replay computation includes the entire history; it is not one-step online update latency."]) if mode == "all": calls = [] for call,(pcm,sr) in audio_cache.items(): points = continuous(pcm,sr) cs = sorted([r for r in rows if r['call_id']==call],key=lambda r:int(r['decision_ms'])) calls.append(dict(call_id=call,audio_path=f'韩泰机器人抢话/音频/{call}.mp3',audio_sha256=audio_hashes[call], duration_ms=round(len(pcm)*1000/sr,3),points=points, checkpoints=[[int(r['decision_ms']),round(r['can_reply_score'],7)] for r in cs])) print(f"{call[:8]} timeline: {len(points)} scores, {len(cs)} checkpoints",flush=True) summary['timeline_points'] = sum(len(c['points']) for c in calls) exported = dict(schema_version=1,model='maai_mimi_mc' if MC else 'maai_mimi_standard', name='VAP / MaAI Mimi(降噪双声道)' if MC else 'VAP / MaAI Mimi(标准双声道)', score='1 - p_now[user]',sample_interval_ms=FRAME_MS,method=summary['method'], provenance=summary['provenance'],calls=calls) assert prov['label_sha256']==sha(LABELS) and prov['history_sha256']==sha(HISTORY),'Annotations changed during inference' write(OUT/'predictions.csv',rows) errors = [r for r in rows if not r['correct_05']] if errors:write(OUT/'errors.csv',errors) else:(OUT/'errors.csv').unlink(missing_ok=True) write(OUT/'thresholds.csv',thresholds) summary['elapsed_seconds'] = time.perf_counter()-began if mode=='all':(OUT/'score-timeline.json').write_text(json.dumps(exported,ensure_ascii=False,separators=(',',':'))+'\n') (OUT/'summary.json').write_text(json.dumps(summary,ensure_ascii=False,indent=2)+'\n') print(json.dumps({k:summary[k] for k in ('main_metrics','cpc_mc_stereo_reference','changed_vs_cpc_mc_stereo','elapsed_seconds')},ensure_ascii=False,indent=2)) if mode=='all': from evaluate_streaming import refresh_streaming streaming, _ = refresh_streaming('maai_mimi_mc' if MC else 'maai_mimi_standard') print('PRIMARY streaming metrics:',json.dumps(streaming['main_metrics']),flush=True) if __name__=='__main__': parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('mode',choices=('smoke','evaluate','all')) parser.add_argument('--variant',choices=('standard','mc','both'),default='standard') args=parser.parse_args() torch.set_num_threads(4) torch.set_num_interop_threads(1) for selected in (('standard','mc') if args.variant=='both' else (args.variant,)): main(args.mode,selected)