Files
sundynix-agentix/sundynix-dispatcher/internal/eino/board_serde.go
T
Blizzard 5a1a994cc2 feat(hitl): 增量2a —— board 可序列化(compose checkpoint 的状态载体)
compose checkpoint 把图执行态(含 GenLocalState 的 board)序列化进 store,但
Eino 序列化器只认导出字段,而 board 字段全未导出 → 直接持久化会落成空、resume
丢全部状态。Eino 对同时实现 json.Marshaler+Unmarshaler 的类型改走自定义 JSON
(internal/serialization checkMarshaler),故给 *board 实现一对 JSON 方法映射到
导出 DTO,无需把 board 字段全导出(牵连几十处调用点)。

- board_serde.go: *board 的 MarshalJSON/UnmarshalJSON ↔ boardSnapshot;丢弃
  fatalErr(transient error,中断点必为 nil);schema.RegisterName[*board] 注册
  类型名供 checkpoint 的 State(any) 还原。
- 测试: 快照往返无损 + 编译期断言实现 json.Marshaler/Unmarshaler + fatalErr
  不被持久化。

零行为变更(纯新增)。go test ./... 全绿。
下一步 2b:审批节点 compose.Interrupt + 编译挂 checkpoint store + orchestrator
识别 InterruptInfo 置 waiting 并释放 goroutine。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:38:23 +08:00

76 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package eino
import (
"encoding/json"
"github.com/cloudwego/eino/schema"
)
// board 的字段全是未导出的,而 Eino 的 checkpoint 序列化器只认导出字段——若直接让 compose
// 持久化 board,会落成空、resume 丢全部执行态。Eino 对「同时实现 json.Marshaler+Unmarshaler」
// 的类型改走自定义 JSON(见 internal/serialization checkMarshaler),故给 *board 实现一对 JSON
// 方法、映射到导出 DTO,即可把整个黑板按需持久化,而无需把 board 字段全导出(牵连几十处调用点)。
//
// 不持久化 fatalErrerrortransient——中断点处必为 nil):审批中断时图尚未失败,
// 它是单次 Invoke 内的致命错标志,跨 resume 无意义。rejected 同理在中断点为 false
// 但仍带上以求快照完整、零歧义。
type boardSnapshot struct {
UID string `json:"uid,omitempty"`
SID string `json:"sid,omitempty"`
Query string `json:"query,omitempty"`
Profile string `json:"profile,omitempty"`
History []*schema.Message `json:"history,omitempty"`
KB string `json:"kb,omitempty"`
Refs []string `json:"refs,omitempty"`
ToolOut []string `json:"tool_out,omitempty"`
Sections []reportSection `json:"sections,omitempty"`
Answer string `json:"answer,omitempty"`
AgentOut []string `json:"agent_out,omitempty"`
Rejected bool `json:"rejected,omitempty"`
}
// MarshalJSON 把黑板序列化为快照(Eino checkpoint 经此持久化整图执行态)。
func (b *board) MarshalJSON() ([]byte, error) {
return json.Marshal(boardSnapshot{
UID: b.uid,
SID: b.sid,
Query: b.query,
Profile: b.profile,
History: b.history,
KB: b.kb,
Refs: b.refs,
ToolOut: b.toolOut,
Sections: b.sections,
Answer: b.answer,
AgentOut: b.agentOut,
Rejected: b.rejected,
})
}
// UnmarshalJSON 从快照还原黑板(resume 时 Eino 据此重建中断前的执行态)。
func (b *board) UnmarshalJSON(data []byte) error {
var s boardSnapshot
if err := json.Unmarshal(data, &s); err != nil {
return err
}
b.uid = s.UID
b.sid = s.SID
b.query = s.Query
b.profile = s.Profile
b.history = s.History
b.kb = s.KB
b.refs = s.Refs
b.toolOut = s.ToolOut
b.sections = s.Sections
b.answer = s.Answer
b.agentOut = s.AgentOut
b.rejected = s.Rejected
return nil
}
// 注册 *board 的序列化类型名:Eino checkpoint 的 State 是 any,持久化时记类型名、
// resume 时据名还原具体类型再走 UnmarshalJSON。名字一经上线不可改(旧 checkpoint 据此解码)。
func init() {
schema.RegisterName[*board]("sundynix_board")
}