Vibrato / vib_state.py
Corolin's picture
feat: initial release of vibrato model artifacts (with LFS for weights and tokenizer)
79c6440
Raw History Blame Contribute Delete
2.82 kB
# -*- coding: utf-8 -*-
"""Vibrato 上下文文本构建 — 训练输入与 teacher 输入同源(逐字符一致)。
parity 纪律: net 看到的 char id 序列来自这个文本, teacher 判断的也是这个文本,
二者必须是同一个字符串(含 TARGET 标记), 由构造保证。
角色: [用户]=被分析的 them(情绪载体), [AI]=chordia。
"""
from __future__ import annotations
import re
from typing import Dict, List
MAX_CONTEXT_MSGS = 6 # 目标之前最多带 6 条
_CRLF = re.compile(r"\r\n?")
_BLANK = re.compile(r"\n{3,}")
def sanitize(text: str) -> str:
return _BLANK.sub("\n\n", _CRLF.sub("\n", text)).strip()
def role_of(side: str) -> str:
"""user→[用户](情绪载体/them), ai→[AI](chordia/self)。"""
return "[用户]" if side == "user" else "[AI]"
def build_context(messages: List[Dict[str, str]], target_idx: int,
max_context: int = MAX_CONTEXT_MSGS) -> str:
"""产出带 TARGET 标记的上下文文本(旧→新, 目标收尾)。
messages: [{side: "user"|"ai", text}], target_idx 指向被标注的 user 消息。
"""
if messages[target_idx]["side"] != "user":
raise ValueError("target 必须是 user 侧消息(情绪载体)")
start = max(0, target_idx - max_context)
lines = []
for i in range(start, target_idx + 1):
m = messages[i]
tag = "TARGET(需判断): " if i == target_idx else ""
lines.append(f"{tag}{role_of(m['side'])}: {sanitize(m['text'])}")
return "\n".join(lines)
def echo_from_prev(pad_norm_3: List[float]) -> List[float]:
"""上一条 user 消息的归一化 PAD → echo(截到[-1,1]; 无历史返回零向量)。"""
if not pad_norm_3:
return [0.0, 0.0, 0.0]
return [max(-1.0, min(1.0, float(v))) for v in pad_norm_3[:3]]
if __name__ == "__main__":
demo = [
{"side": "ai", "text": "今天过得怎么样呀?"},
{"side": "user", "text": "还行吧,就那样\r\n\n\n\n一堆破事"},
{"side": "ai", "text": "听起来有点烦,想说说吗?"},
{"side": "user", "text": "算了不说了,你陪我聊点别的"},
{"side": "ai", "text": "好呀,那就聊点开心的~"},
{"side": "user", "text": "嗯"},
]
text = build_context(demo, 5)
print(text)
assert "TARGET(需判断): [用户]: 嗯" in text
assert "一堆破事" in text and "\r" not in text and "\n\n\n" not in text
assert echo_from_prev(None) == [0.0, 0.0, 0.0]
assert echo_from_prev([0.5, -2, 1]) == [0.5, -1.0, 1.0]
# 窗口裁剪: 目标(偶数idx=user)前只带 6 条
long = [{"side": "user" if i % 2 == 0 else "ai", "text": f"m{i}"} for i in range(20)]
ctx = build_context(long, 18)
assert "m11" not in ctx and "m12" in ctx
print("✓ vib_state 自检通过")