feat(gateway): 消息mode落库(A3) — 持久化真实写入渲染模式
- POST /api/v1/chat 与 ws.chat 上行新增可选 mode(行为模式 design/01 §3/design/16 §5.1) - Web路径经ctx透传本轮mode到编排器持久化(persistUserMessages/persistAssistantReply),缺省回落chat - pendingMsg记录mode,恢复/续接路径沿用发起轮次mode - IM路径(persistToMain/persistIsolated)恒为chat - 新增/扩充测试:REST/WS/orchestrator/channelbridge 覆盖mode写入 - Docs/api-client.md v0.20 同步mode字段
This commit is contained in:
1 parent
fe5dbcddba
commit
b686d5fec8
9 files changed
+199
-41
No files matched your search
@@ -235,9 +235,10 @@ func (h *hub) streamChat(ctx context.Context, payload any) {
|
||||
// The Web main session is the owner's private conversation (JWT = owner), so
|
||||
// it binds the full-access env: memory read+write, unchanged behavior.
|
||||
msgs := userChatMessages(req.Text, req.Image)
|
||||
// Thread the client's deviceID into the orchestrator via ctx so both the
|
||||
// persisted user turn and the assistant reply are tagged (design/16 §4.5).
|
||||
ctx = withDeviceID(ctx, req.DeviceID)
|
||||
// Thread the client's deviceID and behavior mode into the orchestrator via ctx
|
||||
// so both the persisted user turn and the assistant reply are tagged
|
||||
// (design/16 §4.5, §5.1).
|
||||
ctx = withMode(withDeviceID(ctx, req.DeviceID), req.Mode)
|
||||
if h.orchestrator != nil {
|
||||
_ = h.orchestrator.Submit(ctx, req.SessionID, msgs, loop.OwnerEnv())
|
||||
return
|
||||
@@ -312,6 +313,10 @@ type chatStreamRequest struct {
|
||||
// (design/16 §4.5), e.g. "web:chrome:a1b2c3"; it is optional and persisted
|
||||
// alongside the message so the agent knows which end the owner is chatting from.
|
||||
DeviceID string `json:"deviceId,omitempty"`
|
||||
// Mode is the behavior mode the client sent the turn in (design/01 §3,
|
||||
// design/16 §5.1); it is optional and persisted per message, falling back to
|
||||
// "chat" when omitted.
|
||||
Mode string `json:"mode,omitempty"`
|
||||
}
|
||||
|
||||
// handleWS upgrades /ws/chat after authenticating the JWT from the query token or
|
||||
@@ -439,13 +444,14 @@ func (c *wsConn) enqueueJSON(ev wsEvent) {
|
||||
}
|
||||
|
||||
// publishChatStream publishes a ws.chat streaming trigger (REST stream mode).
|
||||
// deviceID is the sending browser/device id carried on the payload so the ws.chat
|
||||
// consumer can tag the persisted turn (design/16 §4.5).
|
||||
func (s *Server) publishChatStream(ctx context.Context, sessionID, text, image, deviceID string) {
|
||||
// deviceID is the sending browser/device id and mode the behavior mode the client
|
||||
// sent the turn in, both carried on the payload so the ws.chat consumer can tag
|
||||
// the persisted turn (design/16 §4.5, §5.1).
|
||||
func (s *Server) publishChatStream(ctx context.Context, sessionID, text, image, deviceID, mode string) {
|
||||
if s.bus == nil {
|
||||
return
|
||||
}
|
||||
_ = s.bus.Publish(ctx, wsChatSubject, map[string]any{"sessionId": sessionID, "text": text, "image": image, "deviceId": deviceID})
|
||||
_ = s.bus.Publish(ctx, wsChatSubject, map[string]any{"sessionId": sessionID, "text": text, "image": image, "deviceId": deviceID, "mode": mode})
|
||||
}
|
||||
|
||||
// wsToken extracts the JWT from the ?token= query or the Authorization header.
|
||||
|
||||
Reference in new issue
Block a user