refactor(session): replace dm scope with dimensions policy

This commit is contained in:
Hoshina
2026-04-01 17:19:50 +08:00
parent 3957e2cc72
commit ca9652e120
20 changed files with 568 additions and 124 deletions
+28 -8
View File
@@ -17,10 +17,8 @@ type RouteInput struct {
}
// SessionPolicy describes how a routed message should be mapped to a session.
// The current implementation preserves the legacy dm_scope and identity_link
// semantics while moving session-key construction out of the router.
type SessionPolicy struct {
DMScope DMScope
Dimensions []string
IdentityLinks map[string][]string
}
@@ -246,16 +244,38 @@ func (r *RouteResolver) resolveDefaultAgentID() string {
}
func (r *RouteResolver) sessionPolicy() SessionPolicy {
dmScope := DMScope(r.cfg.Session.DMScope)
if dmScope == "" {
dmScope = DMScopeMain
}
return SessionPolicy{
DMScope: dmScope,
Dimensions: normalizeSessionDimensions(r.cfg.Session.Dimensions),
IdentityLinks: cloneIdentityLinks(r.cfg.Session.IdentityLinks),
}
}
func normalizeSessionDimensions(dimensions []string) []string {
if len(dimensions) == 0 {
return nil
}
normalized := make([]string, 0, len(dimensions))
seen := make(map[string]struct{}, len(dimensions))
for _, dimension := range dimensions {
dimension = strings.ToLower(strings.TrimSpace(dimension))
switch dimension {
case "space", "chat", "topic", "sender":
default:
continue
}
if _, ok := seen[dimension]; ok {
continue
}
seen[dimension] = struct{}{}
normalized = append(normalized, dimension)
}
if len(normalized) == 0 {
return nil
}
return normalized
}
func cloneIdentityLinks(src map[string][]string) map[string][]string {
if len(src) == 0 {
return nil
+3 -3
View File
@@ -17,7 +17,7 @@ func testConfig(agents []config.AgentConfig, bindings []config.AgentBinding) *co
},
Bindings: bindings,
Session: config.SessionConfig{
DMScope: "per-peer",
Dimensions: []string{"sender"},
},
}
}
@@ -37,8 +37,8 @@ func TestResolveRoute_DefaultAgent_NoBindings(t *testing.T) {
if route.MatchedBy != "default" {
t.Errorf("MatchedBy = %q, want 'default'", route.MatchedBy)
}
if route.SessionPolicy.DMScope != DMScopePerPeer {
t.Errorf("SessionPolicy.DMScope = %q, want %q", route.SessionPolicy.DMScope, DMScopePerPeer)
if len(route.SessionPolicy.Dimensions) != 1 || route.SessionPolicy.Dimensions[0] != "sender" {
t.Errorf("SessionPolicy.Dimensions = %v, want [sender]", route.SessionPolicy.Dimensions)
}
if route.SessionPolicy.IdentityLinks != nil {
t.Errorf("SessionPolicy.IdentityLinks = %v, want nil", route.SessionPolicy.IdentityLinks)
+13
View File
@@ -112,6 +112,19 @@ func CanonicalSessionPeerID(
return strings.ToLower(normalizedPeerID)
}
// CanonicalSessionIdentityID collapses an identity using identity_links when
// possible, then returns a normalized lowercase identifier.
func CanonicalSessionIdentityID(channel, rawID string, identityLinks map[string][]string) string {
normalizedID := strings.TrimSpace(rawID)
if normalizedID == "" {
return ""
}
if linked := resolveLinkedPeerID(identityLinks, channel, normalizedID); linked != "" {
normalizedID = linked
}
return strings.ToLower(normalizedID)
}
// ParseAgentSessionKey extracts agentId and rest from "agent:<agentId>:<rest>".
func ParseAgentSessionKey(sessionKey string) *ParsedSessionKey {
raw := strings.TrimSpace(sessionKey)