test(memory)!: 块 union 化判据重写(CAS 淘汰第二阶段:测试)

生产已在上一提交(86572d13)全绿;本提交把 CAS 专属测试改到块语义,
并修掉几处判据自身的缺陷(判据错 ≠ 生产错):

- sdk/memory_impl_test: media.Store→块;NewGraphMemoryWithPlugin
- core/{graphmedia,mediaref,medialive,medialoop,inputunify}_test:
  mediaStore→memory;PayloadDigest→Data/BlockData;shortDigest→shortID
- graphmedia: 无引用句子用独立块 ID(shortID 20 字符会与 blk_src_ 撞长度)
- memory/sentence_block: BlockByIDPrefix 加 len<12 下限(短引用守卫)
- core/medialoop: reembedOneBlock 按 BlockData 兜底取字节(lean 列无 data)
- knowledge: broken.png 改目录造读失败(chmod 0000 对 root 无效);
  dense/attach 测试补 Data 并先落盘后 AttachMedia
- webui: graph_pulse fixture payload_digest→has_data

注:internal/plugin/proc 的 shmVersion 不匹配(内核1/插件2)是
SDK 仓(777c3a0,v2 模板)与本分支内核(v1)的跨仓版本错位,
与块 union 无关(stash 全部改动后仍复现),属 proc-extra 特性线。
This commit is contained in:
JianFeeeee
2026-10-12 14:26:13 +08:00
parent 86572d134d
commit 33e1a0bfaf
16 changed files with 630 additions and 858 deletions

View File

@ -2,6 +2,8 @@ package knowledge
import (
"errors"
"os"
"path/filepath"
"testing"
"github.com/JianFeeeee/HomeAgent/internal/memory/vector"
@ -87,7 +89,9 @@ func TestDenseCrossModalRecall(t *testing.T) {
s.SetDenseSpace(mm)
if err := s.AddWithMedia("cat-photo", "猫 图片 说明", []KnowledgeMediaRef{
{File: "cat.png", MIME: "image/png", Kind: "image"},
// ★ 带 Data:Write 路径会 writeMediaFiles 落盘到条目目录,随后 denseFor
// 读回它算图向量。字节 "d-cat" 对应 fakeMM.img 里的 eax 方向。
{File: "cat.png", MIME: "image/png", Kind: "image", Data: []byte("d-cat")},
}); err != nil {
t.Fatal(err)
}
@ -291,6 +295,11 @@ func TestAttachMediaRecomputesDense(t *testing.T) {
if err := s.Add("k", "正文"); err != nil {
t.Fatal(err)
}
// AttachMedia 是「挂已在盘上的媒体」:字节必须先落到条目目录,
// 否则 denseFor 读不到。"d-x" 对应 fakeMM.img 里的 eax 方向。
if err := os.WriteFile(filepath.Join(dir, "k", "x.png"), []byte("d-x"), 0644); err != nil {
t.Fatal(err)
}
before := append([]float64{}, s.items["k"].Dense...)
if err := s.AttachMedia("k", KnowledgeMediaRef{File: "x.png", MIME: "image/png"}); err != nil {
t.Fatal(err)

View File

@ -448,16 +448,16 @@ func TestImportDirMediaSkippedWhenNotRequested(t *testing.T) {
func TestImportDirMediaFailureDoesNotFailEntry(t *testing.T) {
src := mkSrc(t, map[string]string{"note.md": "正文"})
s, _ := newImportStore(t)
// 造一个指向不存在文件的媒体候选:ImportDir 收集目录时按扩展名识别,
// 读不出的文件走 putMediaFile 错误路径,只跳过该媒体。
// 造一个读不出的媒体候选:ImportDir 按扩展名识别,读不出的文件
// 走 putMediaFile 错误路径,只跳过该媒体。
// ★ 用「把 broken.png 做成目录」来造读失败:os.ReadFile 对目录恒报错,
// 与权限/身份无关。chmod 0000 对 root 无效(root 绕过 DAC),
// 以前那种写法在 root 下跑时 putMediaFile 会成功 → broken.png 变成
// 独立媒体条目 → Imported=2,假阳性。
bad := filepath.Join(src, "broken.png")
if err := os.WriteFile(bad, []byte("x"), 0644); err != nil {
if err := os.Mkdir(bad, 0755); err != nil {
t.Fatal(err)
}
if err := os.Chmod(bad, 0000); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = os.Chmod(bad, 0644) })
st, err := s.ImportDir(ImportOptions{Dir: src, IncludeMedia: true})
if err != nil {

View File

@ -77,9 +77,9 @@ func TestMultimodalEndToEndWithRealProvider(t *testing.T) {
s.SetDenseSpace(ds)
img := mmTinyPNG(t)
if err := s.AddWithMedia("photo", "一只猫的照片", []KnowledgeMediaRef{
{File: "photo.png", MIME: "image/png", Kind: "image"},
}); err != nil {
// 先建纯文本条目:字节还没落盘,现在注册媒体引用会让 denseFor
// 读不到文件。落盘后再用 AttachMedia 挂上并重算 —— 这样 Media 恰好 1 条。
if err := s.Add("photo", "一只猫的照片"); err != nil {
t.Fatal(err)
}
// 字节落进条目目录(CAS 淘汰后:媒体随条目生灭)