feat(gateway,loop): 忙时并行回合基础管线(I1′ 有条件放宽)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- turnId 由编排器生成、loop Run 复用(硬前提:否则 turn_begin 与流帧各铸一个,客户端两级分桶立即失配)
- StateSink 增带 turnId 的变体(未接线时退化为现签名,不丢状态帧)
- 上行 chat.parallel(WS chat 帧 + REST 三入口同形;省略 = 逐字不变)
- 单会话额度默认 2 / 硬上限 3 / <2 关闭;额度满或未声明 ⇒ 落回既有排队语义
- I1′:checkpoint 归属、历史配对、忙态计数三处从会话级单例升为路级
- session_idle 收紧为「所有在途路收尾且 pending 空」(仅并行时生效;单路逐字不变)
- 预留接口(形状定死、无实现无接线):tool/legfile.go 的 FileClaimer / ErrFileBusy / WithLegID
- 顺手修一个真 bug:并行路 ctx 派生自提交请求作用域,REST 同步入口一写响应就把它连坐杀死(改 context.WithoutCancel 派生)
- 23 个新用例(含 -race -count=3);变异 6 条(turnId 复用 / idle 时点 / 并行升级 / checkpoint 键 / 编排器交 id / pending_ack reason)
- 契约变更点 10 条待监工升 v0.65(本人未改 Docs/**)
This commit is contained in:
AskaEth committed 2026-10-05 09:18:50 +08:00
1 parent 7d2ff74cfc
commit 89e13cf53d
23 files changed
+1921 -275

No files matched your search

+46 -35
View File
@@ -93,6 +93,9 @@ type Server struct {
// vector 是组合根报告的 pgvector 增强运行时状态(见
// Deps.Vector / VectorState)。零值 = 未配置。
vector VectorState
// maxParallelTurns 是单会话在途并行路数的上限(见 Deps.MaxParallelTurns)。
// 零值 = 未接线 ⇒ 编排器用默认 2。
maxParallelTurns int
// envResolver 解析会话每轮的 loop 环境(其角色)。见
// persona_env.go / design/22 §4.5。
envResolver turnEnvResolver
@@ -111,38 +114,39 @@ func New(cfg *config.Config, deps Deps) (*Server, error) {
return nil, fmt.Errorf("gateway: auth dependency required")
}
s := &Server{
cfg: cfg,
auth: deps.Auth,
bus: deps.Bus,
caps: deps.Registry,
runner: deps.Loop,
sessions: deps.Session,
personas: deps.Personas,
memories: deps.Memories,
history: deps.History,
iot: deps.IoT,
auditor: deps.Scheduler,
taskOutputs: deps.Tasks,
automations: deps.Automations,
users: deps.Users,
refresh: deps.Refresh,
backthink: deps.Backthink,
rag: deps.RAG,
obs: newObservability(deps),
streaming: deps.Streaming,
frontend: deps.Frontend,
llm: deps.LLM,
envPath: deps.EnvPath,
channels: deps.Channels,
plugins: deps.Plugins,
ownerRegistrar: deps.OwnerRegistrar,
webhooks: deps.Webhooks,
usage: deps.Usage,
dbPath: deps.DatabasePath,
dataDir: deps.DataDir,
db: deps.Database,
vector: deps.Vector,
log: sourcedLog(deps.Log),
cfg: cfg,
auth: deps.Auth,
bus: deps.Bus,
caps: deps.Registry,
runner: deps.Loop,
sessions: deps.Session,
personas: deps.Personas,
memories: deps.Memories,
history: deps.History,
iot: deps.IoT,
auditor: deps.Scheduler,
taskOutputs: deps.Tasks,
automations: deps.Automations,
users: deps.Users,
refresh: deps.Refresh,
backthink: deps.Backthink,
rag: deps.RAG,
obs: newObservability(deps),
streaming: deps.Streaming,
frontend: deps.Frontend,
llm: deps.LLM,
envPath: deps.EnvPath,
channels: deps.Channels,
plugins: deps.Plugins,
ownerRegistrar: deps.OwnerRegistrar,
webhooks: deps.Webhooks,
usage: deps.Usage,
dbPath: deps.DatabasePath,
dataDir: deps.DataDir,
db: deps.Database,
vector: deps.Vector,
log: sourcedLog(deps.Log),
maxParallelTurns: deps.MaxParallelTurns,
}
// 一个角色解析器由 Server 各处理器与 hub 共用,使每个面向 owner
// 的入口(REST、WS、语音)以同样方式解析会话角色(design/22 §4.5),
@@ -208,9 +212,16 @@ func newOrchestrator(s *Server) *Orchestrator {
Emit: func(sessionID, typ string, data map[string]any) {
s.hub.broadcast(wsEvent{Type: typ, SessionID: sessionID, Data: data})
},
OnPersisted: s.BroadcastPersisted,
Log: s.log,
History: s.history,
// EmitTurn 是带 turnId 的变体(design/15 §2.5.9):帧壳上的 turnId 让客户端
// 把「这条状态帧属于哪一路」与流帧对齐 —— 真并行下同一会话有两条交错的路,
// 只按 sessionId 分桶会把它们糊在一起。
EmitTurn: func(sessionID, turnID, typ string, data map[string]any) {
s.hub.broadcast(wsEvent{Type: typ, SessionID: sessionID, TurnID: turnID, Data: data})
},
OnPersisted: s.BroadcastPersisted,
Log: s.log,
History: s.history,
MaxParallelTurns: s.maxParallelTurns,
})
}