Files
mattermore/server/configuration.go
T

129 lines
3.8 KiB
Go

package main
import (
"fmt"
"net/url"
"strings"
)
// configuration holds the plugin settings from the System Console.
type configuration struct {
OllamaURL string
DefaultModel string
Persona string
CustomPersona string
Brevity string
RepeatPenalty int
FrequencyPenalty int
MaxTokens int
RateLimitPerMinute int
AllowedUserIDs string
StopWords string
DebugLogging bool
}
// getConfiguration returns the current configuration, safe for concurrent use.
func (p *Plugin) getConfiguration() *configuration {
p.configLock.RLock()
defer p.configLock.RUnlock()
return p.configuration
}
// setConfiguration updates the configuration and validates it.
func (p *Plugin) setConfiguration(c *configuration) error {
p.configLock.Lock()
defer p.configLock.Unlock()
if c == nil {
return fmt.Errorf("nil configuration")
}
if err := c.validate(); err != nil {
return fmt.Errorf("invalid configuration: %w", err)
}
p.configuration = c
return nil
}
// validate checks that the configuration values are usable.
func (c *configuration) validate() error {
if c.OllamaURL == "" {
return fmt.Errorf("OllamaURL must not be empty")
}
u, err := url.Parse(c.OllamaURL)
if err != nil {
return fmt.Errorf("OllamaURL is not a valid URL: %w", err)
}
if u.Scheme != "http" && u.Scheme != "https" {
return fmt.Errorf("OllamaURL must use http or https scheme, got %q", u.Scheme)
}
if c.DefaultModel == "" {
return fmt.Errorf("DefaultModel must not be empty")
}
if c.MaxTokens < 0 {
return fmt.Errorf("MaxTokens must not be negative")
}
if c.RateLimitPerMinute < 0 {
return fmt.Errorf("RateLimitPerMinute must not be negative")
}
return nil
}
// OnConfigurationChange is called by the Mattermost server when the plugin
// configuration is changed in the System Console.
func (p *Plugin) OnConfigurationChange() error {
var c configuration
// Load from the System Console settings.
if err := p.API.LoadPluginConfiguration(&c); err != nil {
p.API.LogError("Config load failed", "error", err.Error())
return fmt.Errorf("failed to load plugin configuration: %w", err)
}
if c.DebugLogging {
p.API.LogInfo("Config loaded", "model", c.DefaultModel, "persona", c.Persona, "brevity", c.Brevity, "repeat", c.RepeatPenalty, "freq", c.FrequencyPenalty, "tokens", c.MaxTokens)
}
if err := p.setConfiguration(&c); err != nil {
p.API.LogError("Config validate failed", "error", err.Error())
return fmt.Errorf("failed to set configuration: %w", err)
}
// Pass the debug logging flag to the Ollama client.
if p.ollamaClient != nil {
p.ollamaClient.SetLogging(c.DebugLogging)
}
return nil
}
// allowedUsers returns the set of user IDs allowed to use the plugin.
// An empty set means all users are allowed.
func (c *configuration) allowedUsers() map[string]bool {
if strings.TrimSpace(c.AllowedUserIDs) == "" {
return nil
}
users := make(map[string]bool)
for _, id := range strings.Split(c.AllowedUserIDs, ",") {
users[strings.TrimSpace(id)] = true
}
return users
}
// stopWordSet returns the set of stop words for the engagement engine.
func (c *configuration) stopWordSet() map[string]bool {
if strings.TrimSpace(c.StopWords) == "" {
return map[string]bool{
"thanks": true, "thank you": true, "bye": true,
"goodbye": true, "that's all": true, "done": true,
"stop": true, "quit": true, "end": true,
}
}
words := make(map[string]bool)
for _, w := range strings.Split(c.StopWords, ",") {
words[strings.TrimSpace(strings.ToLower(w))] = true
}
return words
}