refactor(memory)!: 块 union 化,彻底淘汰 CAS 第一阶段(生产代码)

块自带负载(MemoryBlock.Data),删 PayloadDigest;检索纯向量,媒体随块
同生灭。生产代码全绿;CAS 专属测试将在下一提交随判据重写。

- memory: Data BLOB + migration9;blockColumns 不含 data(召回只比向量);
  MediaBlockID 内容派生 ID;BlockByIDPrefix/BlockData 按需加载
- sdk: memory_impl 去 media.Store;MemoryBlockView PayloadDigest→HasData
- core: mediaref/graphmedia/crossmodal/medialoop 全部改块语义;
  captureBlockMedia 直接产块;resolveMediaRefs 按块 ID 解析
- knowledge: 媒体字节随条目目录自带(File+Data),删 mediaGet/mediaPut
- webui: graphBlockView/pulse payload_digest→has_data
- bootstrap/registry: 去 MediaStore 接线(SetMediaStore 空实现,待⑥删包)
This commit is contained in:
JianFeeeee
2026-10-12 10:13:36 +08:00
parent b15994d964
commit 86572d134d
30 changed files with 806 additions and 864 deletions

View File

@ -44,7 +44,7 @@ func (f *fakeGraphMemory) BlocksChangedSince(since time.Time, limit int) ([]sdk.
}
out = append(out, sdk.MemoryBlockView{
ID: b.ID, Modality: string(b.Modality), Text: b.Text,
PayloadDigest: b.PayloadDigest, MIME: b.MIME,
HasData: len(b.Data) > 0, MIME: b.MIME,
Source: b.Source, Tool: b.Tool, Scene: b.Scene,
CreatedAt: b.CreatedAt, UpdatedAt: b.UpdatedAt,
})
@ -74,10 +74,10 @@ func graphFixture() map[string]interface{} {
// 修复前 pulse 读的是它,而它永远落在窗口外 ⇒ 恒返回空。
"memory_blocks": []memory.MemoryBlock{
{ID: "b_old", Modality: memory.BlockModality("text"), Text: "老块",
PayloadDigest: "d0", Vector: []float64{0.1, 0.2, 0.3},
Vector: []float64{0.1, 0.2, 0.3},
CreatedAt: old, UpdatedAt: now.Add(-8 * time.Hour)},
{ID: "b_new", Modality: memory.BlockModality("text"), Text: "刚学到的东西",
PayloadDigest: "d1", Vector: []float64{0.1, 0.2, 0.3},
Vector: []float64{0.1, 0.2, 0.3},
CreatedAt: now, UpdatedAt: now},
},
}
@ -226,7 +226,7 @@ func TestGraphPulse_IgnoresFrozenLegacyNodes(t *testing.T) {
// 块表:有一条刚更新(活图谱的真实形态)
"memory_blocks": []memory.MemoryBlock{
{ID: "b_live", Modality: memory.BlockModality("text"), Text: "刚写入的块",
PayloadDigest: "dx", CreatedAt: now, UpdatedAt: now},
CreatedAt: now, UpdatedAt: now},
},
}

View File

@ -127,20 +127,20 @@ func (h *Handler) handleMemoryTools(w http.ResponseWriter, r *http.Request) {
// 所以这里显式裁掉 vector,而不是让 GraphData 返回值带个开关:
// 本接口的语义就是「图谱的可视化数据」,让唯一调用方拿到它要的东西。
type graphBlockView struct {
ID string `json:"id"`
Modality string `json:"modality"`
Text string `json:"text,omitempty"`
PayloadDigest string `json:"payload_digest"`
MIME string `json:"mime,omitempty"`
Size int64 `json:"size"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
Source string `json:"source,omitempty"`
Tool string `json:"tool,omitempty"`
Scene string `json:"scene,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID string `json:"id"`
Modality string `json:"modality"`
Text string `json:"text,omitempty"`
HasData bool `json:"has_data"`
MIME string `json:"mime,omitempty"`
Size int64 `json:"size"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
Source string `json:"source,omitempty"`
Tool string `json:"tool,omitempty"`
Scene string `json:"scene,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// graphDataForVisual 把 GraphData 的原始 map 裁成可视化视图(去掉稠密向量)。
@ -162,20 +162,20 @@ func graphDataForVisual(data map[string]interface{}) map[string]interface{} {
for i := range blocks {
b := blocks[i]
views = append(views, graphBlockView{
ID: b.ID,
Modality: string(b.Modality),
Text: b.Text,
PayloadDigest: b.PayloadDigest,
MIME: b.MIME,
Size: b.Size,
Width: b.Width,
Height: b.Height,
Fingerprint: b.Fingerprint,
Source: b.Source,
Tool: b.Tool,
Scene: b.Scene,
CreatedAt: b.CreatedAt,
UpdatedAt: b.UpdatedAt,
ID: b.ID,
Modality: string(b.Modality),
Text: b.Text,
HasData: len(b.Data) > 0,
MIME: b.MIME,
Size: b.Size,
Width: b.Width,
Height: b.Height,
Fingerprint: b.Fingerprint,
Source: b.Source,
Tool: b.Tool,
Scene: b.Scene,
CreatedAt: b.CreatedAt,
UpdatedAt: b.UpdatedAt,
})
}
out["memory_blocks"] = views
@ -243,16 +243,16 @@ func (h *Handler) handleMemoryGraphPulse(w http.ResponseWriter, r *http.Request)
pulse := make([]map[string]interface{}, 0, len(blocks))
for _, b := range blocks {
pulse = append(pulse, map[string]interface{}{
"id": b.ID,
"name": b.Text,
"modality": string(b.Modality),
"payload_digest": b.PayloadDigest,
"mime": b.MIME,
"tool": b.Tool,
"scene": b.Scene,
"source": b.Source,
"updated_at": b.UpdatedAt,
"created_at": b.CreatedAt,
"id": b.ID,
"name": b.Text,
"modality": b.Modality,
"has_data": b.HasData,
"mime": b.MIME,
"tool": b.Tool,
"scene": b.Scene,
"source": b.Source,
"updated_at": b.UpdatedAt,
"created_at": b.CreatedAt,
})
}
writeJSON(w, http.StatusOK, map[string]interface{}{