mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
fix: wire dm_scope into runtime session isolation dimensions
The dm_scope field was stored in config but never translated into the dimensions array that the routing layer actually consumes. This meant changing the session isolation scope in the UI had no effect at runtime. Add ApplyDmScope() to SessionConfig which maps the user-facing dm_scope values (per-channel-peer, per-channel, per-peer, global) to the corresponding dimension arrays. Call it in LoadConfig post-processing and in both the PATCH and PUT API handlers. Includes table-driven tests covering all dm_scope values and the precedence rule (explicit dimensions > derived from dm_scope).
This commit is contained in:
@@ -352,6 +352,26 @@ type SessionConfig struct {
|
||||
DmScope string `json:"dm_scope,omitempty"`
|
||||
}
|
||||
|
||||
// ApplyDmScope translates the user-facing dm_scope value into the internal
|
||||
// dimensions array that the routing layer consumes. It is a no-op when
|
||||
// DmScope is empty or when Dimensions is already set (explicit Dimensions
|
||||
// take precedence over the derived value).
|
||||
func (s *SessionConfig) ApplyDmScope() {
|
||||
if s.DmScope == "" || len(s.Dimensions) > 0 {
|
||||
return
|
||||
}
|
||||
switch s.DmScope {
|
||||
case "per-channel-peer":
|
||||
s.Dimensions = []string{"chat", "sender"}
|
||||
case "per-channel":
|
||||
s.Dimensions = []string{"chat"}
|
||||
case "per-peer":
|
||||
s.Dimensions = []string{"sender"}
|
||||
case "global":
|
||||
s.Dimensions = nil
|
||||
}
|
||||
}
|
||||
|
||||
// RoutingConfig controls the intelligent model routing feature.
|
||||
// When enabled, each incoming message is scored against structural features
|
||||
// (message length, code blocks, tool call history, conversation depth, attachments).
|
||||
@@ -1477,6 +1497,8 @@ func LoadConfig(path string) (*Config, error) {
|
||||
cfg.Agents.Defaults.Workspace = filepath.Join(homePath, pkg.WorkspaceName)
|
||||
}
|
||||
|
||||
cfg.Session.ApplyDmScope()
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1728,6 +1728,65 @@ func TestDefaultConfig_SessionDimensions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionConfig_ApplyDmScope(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dmScope string
|
||||
dimensions []string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "per-channel-peer",
|
||||
dmScope: "per-channel-peer",
|
||||
want: []string{"chat", "sender"},
|
||||
},
|
||||
{
|
||||
name: "per-channel",
|
||||
dmScope: "per-channel",
|
||||
want: []string{"chat"},
|
||||
},
|
||||
{
|
||||
name: "per-peer",
|
||||
dmScope: "per-peer",
|
||||
want: []string{"sender"},
|
||||
},
|
||||
{
|
||||
name: "global",
|
||||
dmScope: "global",
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "explicit dimensions take precedence",
|
||||
dmScope: "per-channel-peer",
|
||||
dimensions: []string{"sender"},
|
||||
want: []string{"sender"},
|
||||
},
|
||||
{
|
||||
name: "empty dm_scope is no-op",
|
||||
dmScope: "",
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &SessionConfig{
|
||||
DmScope: tt.dmScope,
|
||||
Dimensions: tt.dimensions,
|
||||
}
|
||||
s.ApplyDmScope()
|
||||
if len(s.Dimensions) != len(tt.want) {
|
||||
t.Fatalf("Dimensions = %v, want %v", s.Dimensions, tt.want)
|
||||
}
|
||||
for i, v := range tt.want {
|
||||
if s.Dimensions[i] != v {
|
||||
t.Errorf("Dimensions[%d] = %q, want %q", i, s.Dimensions[i], v)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultConfig_WorkspacePath_Default(t *testing.T) {
|
||||
t.Setenv("PICOCLAW_HOME", "")
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ func (h *Handler) handleUpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, fmt.Sprintf("Invalid JSON: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
cfg.Session.ApplyDmScope()
|
||||
if execAllowRemoteOmitted(body) {
|
||||
cfg.Tools.Exec.AllowRemote = config.DefaultConfig().Tools.Exec.AllowRemote
|
||||
}
|
||||
@@ -181,6 +182,7 @@ func (h *Handler) handlePatchConfig(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, fmt.Sprintf("Merged config is invalid: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
newCfg.Session.ApplyDmScope()
|
||||
|
||||
// Restore security fields (tokens/keys) from the loaded config before validation,
|
||||
// because private fields are lost during JSON round-trip.
|
||||
|
||||
Reference in New Issue
Block a user