fix(bridge): reconstruct relay reply context (#514)
This commit is contained in:
2 files changed
+101
-1
No files matched your search
+44
-1
@@ -60,6 +60,18 @@ type bridgeReplyCtx struct {
|
||||
ReplyCtx string `json:"reply_ctx"`
|
||||
}
|
||||
|
||||
const bridgeReconstructReplyCtxKind = "bridge_reconstruct"
|
||||
|
||||
// bridgeReconstructReplyCtxPayload is a forward-compatible reply envelope for
|
||||
// reconstruct_reply adapters. Receivers should ignore unknown fields.
|
||||
type bridgeReconstructReplyCtxPayload struct {
|
||||
Kind string `json:"kind"`
|
||||
Version int `json:"v"`
|
||||
SenderProject string `json:"sender_project"`
|
||||
TransportChatID string `json:"transport_chat_id"`
|
||||
TransportSessionKey string `json:"transport_session_key,omitempty"`
|
||||
}
|
||||
|
||||
// --- Wire protocol messages ---
|
||||
|
||||
type bridgeMsg struct {
|
||||
@@ -305,13 +317,44 @@ func (bp *BridgePlatform) ReconstructReplyCtx(sessionKey string) (any, error) {
|
||||
if !a.capabilities["reconstruct_reply"] {
|
||||
return nil, fmt.Errorf("bridge: adapter %q does not support reconstruct_reply", platform)
|
||||
}
|
||||
replyCtx, err := buildBridgeReconstructReplyCtx(bp.project, sessionKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bridgeReplyCtx{
|
||||
Platform: platform,
|
||||
SessionKey: sessionKey,
|
||||
ReplyCtx: sessionKey,
|
||||
ReplyCtx: replyCtx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildBridgeReconstructReplyCtx(project, sessionKey string) (string, error) {
|
||||
chatID, err := bridgeTransportChatID(sessionKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
payload := bridgeReconstructReplyCtxPayload{
|
||||
Kind: bridgeReconstructReplyCtxKind,
|
||||
Version: 1,
|
||||
SenderProject: project,
|
||||
TransportChatID: chatID,
|
||||
TransportSessionKey: sessionKey,
|
||||
}
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("bridge: marshal reconstruct reply ctx: %w", err)
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func bridgeTransportChatID(sessionKey string) (string, error) {
|
||||
parts := strings.SplitN(sessionKey, ":", 3)
|
||||
if len(parts) < 2 || parts[1] == "" {
|
||||
return "", fmt.Errorf("bridge: invalid session key %q", sessionKey)
|
||||
}
|
||||
return parts[1], nil
|
||||
}
|
||||
|
||||
func (bp *BridgePlatform) SendCard(ctx context.Context, replyCtx any, card *Card) error {
|
||||
rc, ok := replyCtx.(*bridgeReplyCtx)
|
||||
if !ok {
|
||||
|
||||
@@ -252,6 +252,63 @@ func TestBridge_ReplyRouting(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBridge_ReconstructReplyCtx_RequiresCapability(t *testing.T) {
|
||||
bs, wsURL := startTestBridge(t, "")
|
||||
bp := bs.NewPlatform("advisor-gemini")
|
||||
|
||||
conn := dialWS(t, wsURL, nil)
|
||||
register(t, conn, "bridge", []string{"text"})
|
||||
|
||||
_, err := bp.ReconstructReplyCtx("bridge:1491487450722341088:relay")
|
||||
if err == nil || !strings.Contains(err.Error(), "does not support reconstruct_reply") {
|
||||
t.Fatalf("ReconstructReplyCtx() error = %v, want reconstruct_reply capability error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBridge_ReconstructReplyCtx_UsesStructuredPayload(t *testing.T) {
|
||||
bs, wsURL := startTestBridge(t, "")
|
||||
bp := bs.NewPlatform("advisor-gemini")
|
||||
|
||||
conn := dialWS(t, wsURL, nil)
|
||||
register(t, conn, "bridge", []string{"text", "reconstruct_reply"})
|
||||
|
||||
replyCtx, err := bp.ReconstructReplyCtx("bridge:1491487450722341088:relay")
|
||||
if err != nil {
|
||||
t.Fatalf("ReconstructReplyCtx() error = %v", err)
|
||||
}
|
||||
|
||||
rc, ok := replyCtx.(*bridgeReplyCtx)
|
||||
if !ok {
|
||||
t.Fatalf("reply ctx type = %T, want *bridgeReplyCtx", replyCtx)
|
||||
}
|
||||
if rc.Platform != "bridge" {
|
||||
t.Fatalf("Platform = %q, want bridge", rc.Platform)
|
||||
}
|
||||
if rc.SessionKey != "bridge:1491487450722341088:relay" {
|
||||
t.Fatalf("SessionKey = %q, want relay session key", rc.SessionKey)
|
||||
}
|
||||
|
||||
var payload bridgeReconstructReplyCtxPayload
|
||||
if err := json.Unmarshal([]byte(rc.ReplyCtx), &payload); err != nil {
|
||||
t.Fatalf("unmarshal reply_ctx: %v", err)
|
||||
}
|
||||
if payload.Kind != bridgeReconstructReplyCtxKind {
|
||||
t.Fatalf("kind = %q, want %q", payload.Kind, bridgeReconstructReplyCtxKind)
|
||||
}
|
||||
if payload.Version != 1 {
|
||||
t.Fatalf("version = %d, want 1", payload.Version)
|
||||
}
|
||||
if payload.SenderProject != "advisor-gemini" {
|
||||
t.Fatalf("sender_project = %q, want advisor-gemini", payload.SenderProject)
|
||||
}
|
||||
if payload.TransportChatID != "1491487450722341088" {
|
||||
t.Fatalf("transport_chat_id = %q, want 1491487450722341088", payload.TransportChatID)
|
||||
}
|
||||
if payload.TransportSessionKey != "bridge:1491487450722341088:relay" {
|
||||
t.Fatalf("transport_session_key = %q, want relay session key", payload.TransportSessionKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBridge_CardFallback(t *testing.T) {
|
||||
bs, wsURL := startTestBridge(t, "")
|
||||
|
||||
|
||||
Reference in new issue
Block a user